Go back to index | PHP CodeBrowser

phpseclib/Crypt/RSA.php
  1.    1  <?php
  2.    2 
  3.    3 /**
  4.    4  * Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA.
  5.    5  *
  6.    6  * PHP versions 4 and 5
  7.    7  *
  8.    8  * Here's an example of how to encrypt and decrypt text with this library:
  9.    9  * <code>
  10.   10  * <?php
  11.   11  *    include 'Crypt/RSA.php';
  12.   12  *
  13.   13  *    $rsa = new Crypt_RSA();
  14.   14  *    extract($rsa->createKey());
  15.   15  *
  16.   16  *    $plaintext = 'terrafrost';
  17.   17  *
  18.   18  *    $rsa->loadKey($privatekey);
  19.   19  *    $ciphertext = $rsa->encrypt($plaintext);
  20.   20  *
  21.   21  *    $rsa->loadKey($publickey);
  22.   22  *    echo $rsa->decrypt($ciphertext);
  23.   23  * ?>
  24.   24  * </code>
  25.   25  *
  26.   26  * Here's an example of how to create signatures and verify signatures with this library:
  27.   27  * <code>
  28.   28  * <?php
  29.   29  *    include 'Crypt/RSA.php';
  30.   30  *
  31.   31  *    $rsa = new Crypt_RSA();
  32.   32  *    extract($rsa->createKey());
  33.   33  *
  34.   34  *    $plaintext = 'terrafrost';
  35.   35  *
  36.   36  *    $rsa->loadKey($privatekey);
  37.   37  *    $signature = $rsa->sign($plaintext);
  38.   38  *
  39.   39  *    $rsa->loadKey($publickey);
  40.   40  *    echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';
  41.   41  * ?>
  42.   42  * </code>
  43.   43  *
  44.   44  * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
  45.   45  * of this software and associated documentation files (the "Software"), to deal
  46.   46  * in the Software without restriction, including without limitation the rights
  47.   47  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  48.   48  * copies of the Software, and to permit persons to whom the Software is
  49.   49  * furnished to do so, subject to the following conditions:
  50.   50  *
  51.   51  * The above copyright notice and this permission notice shall be included in
  52.   52  * all copies or substantial portions of the Software.
  53.   53  *
  54.   54  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  55.   55  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  56.   56  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  57.   57  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  58.   58  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  59.   59  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  60.   60  * THE SOFTWARE.
  61.   61  *
  62.   62  * @category  Crypt
  63.   63  * @package   Crypt_RSA
  64.   64  * @author    Jim Wigginton <terrafrost@php.net>
  65.   65  * @copyright 2009 Jim Wigginton
  66.   66  * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
  67.   67  * @link      http://phpseclib.sourceforge.net
  68.   68  */
  69.   69 
  70.   70 /**
  71.   71  * Include Crypt_Random
  72.   72  */
  73.   73 // the class_exists() will only be called if the crypt_random_string function hasn't been defined and
  74.   74 // will trigger a call to __autoload() if you're wanting to auto-load classes
  75.   75 // call function_exists() a second time to stop the include_once from being called outside
  76.   76 // of the auto loader
  77.   77 if (!function_exists('crypt_random_string')) {
  78.   78     include_once 'Random.php';
  79.   79 }
  80.   80 
  81.   81 /**
  82.   82  * Include Crypt_Hash
  83.   83  */
  84.   84 if (!class_exists('Crypt_Hash')) {
  85.   85     include_once 'Hash.php';
  86.   86 }
  87.   87 
  88.   88 /**#@+
  89.   89  * @access public
  90.   90  * @see self::encrypt()
  91.   91  * @see self::decrypt()
  92.   92  */
  93.   93 /**
  94.   94  * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding}
  95.   95  * (OAEP) for encryption / decryption.
  96.   96  *
  97.   97  * Uses sha1 by default.
  98.   98  *
  99.   99  * @see self::setHash()
  100.  100  * @see self::setMGFHash()
  101.  101  */
  102.  102 define('CRYPT_RSA_ENCRYPTION_OAEP',  1);
  103.  103 /**
  104.  104  * Use PKCS#1 padding.
  105.  105  *
  106.  106  * Although CRYPT_RSA_ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards
  107.  107  * compatibility with protocols (like SSH-1) written before OAEP's introduction.
  108.  108  */
  109.  109 define('CRYPT_RSA_ENCRYPTION_PKCS1'2);
  110.  110 /**
  111.  111  * Do not use any padding
  112.  112  *
  113.  113  * Although this method is not recommended it can none-the-less sometimes be useful if you're trying to decrypt some legacy
  114.  114  * stuff, if you're trying to diagnose why an encrypted message isn't decrypting, etc.
  115.  115  */
  116.  116 define('CRYPT_RSA_ENCRYPTION_NONE'3);
  117.  117 /**#@-*/
  118.  118 
  119.  119 /**#@+
  120.  120  * @access public
  121.  121  * @see self::sign()
  122.  122  * @see self::verify()
  123.  123  * @see self::setHash()
  124.  124  */
  125.  125 /**
  126.  126  * Use the Probabilistic Signature Scheme for signing
  127.  127  *
  128.  128  * Uses sha1 by default.
  129.  129  *
  130.  130  * @see self::setSaltLength()
  131.  131  * @see self::setMGFHash()
  132.  132  */
  133.  133 define('CRYPT_RSA_SIGNATURE_PSS',  1);
  134.  134 /**
  135.  135  * Use the PKCS#1 scheme by default.
  136.  136  *
  137.  137  * Although CRYPT_RSA_SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards
  138.  138  * compatibility with protocols (like SSH-2) written before PSS's introduction.
  139.  139  */
  140.  140 define('CRYPT_RSA_SIGNATURE_PKCS1'2);
  141.  141 /**#@-*/
  142.  142 
  143.  143 /**#@+
  144.  144  * @access private
  145.  145  * @see self::createKey()
  146.  146  */
  147.  147 /**
  148.  148  * ASN1 Integer
  149.  149  */
  150.  150 define('CRYPT_RSA_ASN1_INTEGER',     2);
  151.  151 /**
  152.  152  * ASN1 Bit String
  153.  153  */
  154.  154 define('CRYPT_RSA_ASN1_BITSTRING',   3);
  155.  155 /**
  156.  156  * ASN1 Octet String
  157.  157  */
  158.  158 define('CRYPT_RSA_ASN1_OCTETSTRING'4);
  159.  159 /**
  160.  160  * ASN1 Object Identifier
  161.  161  */
  162.  162 define('CRYPT_RSA_ASN1_OBJECT',      6);
  163.  163 /**
  164.  164  * ASN1 Sequence (with the constucted bit set)
  165.  165  */
  166.  166 define('CRYPT_RSA_ASN1_SEQUENCE',   48);
  167.  167 /**#@-*/
  168.  168 
  169.  169 /**#@+
  170.  170  * @access private
  171.  171  * @see self::Crypt_RSA()
  172.  172  */
  173.  173 /**
  174.  174  * To use the pure-PHP implementation
  175.  175  */
  176.  176 define('CRYPT_RSA_MODE_INTERNAL'1);
  177.  177 /**
  178.  178  * To use the OpenSSL library
  179.  179  *
  180.  180  * (if enabled; otherwise, the internal implementation will be used)
  181.  181  */
  182.  182 define('CRYPT_RSA_MODE_OPENSSL'2);
  183.  183 /**#@-*/
  184.  184 
  185.  185 /**
  186.  186  * Default openSSL configuration file.
  187.  187  */
  188.  188 define('CRYPT_RSA_OPENSSL_CONFIG'dirname(__FILE__) . '/../openssl.cnf');
  189.  189 
  190.  190 /**#@+
  191.  191  * @access public
  192.  192  * @see self::createKey()
  193.  193  * @see self::setPrivateKeyFormat()
  194.  194  */
  195.  195 /**
  196.  196  * PKCS#1 formatted private key
  197.  197  *
  198.  198  * Used by OpenSSH
  199.  199  */
  200.  200 define('CRYPT_RSA_PRIVATE_FORMAT_PKCS1'0);
  201.  201 /**
  202.  202  * PuTTY formatted private key
  203.  203  */
  204.  204 define('CRYPT_RSA_PRIVATE_FORMAT_PUTTY'1);
  205.  205 /**
  206.  206  * XML formatted private key
  207.  207  */
  208.  208 define('CRYPT_RSA_PRIVATE_FORMAT_XML'2);
  209.  209 /**
  210.  210  * PKCS#8 formatted private key
  211.  211  */
  212.  212 define('CRYPT_RSA_PRIVATE_FORMAT_PKCS8'8);
  213.  213 /**#@-*/
  214.  214 
  215.  215 /**#@+
  216.  216  * @access public
  217.  217  * @see self::createKey()
  218.  218  * @see self::setPublicKeyFormat()
  219.  219  */
  220.  220 /**
  221.  221  * Raw public key
  222.  222  *
  223.  223  * An array containing two Math_BigInteger objects.
  224.  224  *
  225.  225  * The exponent can be indexed with any of the following:
  226.  226  *
  227.  227  * 0, e, exponent, publicExponent
  228.  228  *
  229.  229  * The modulus can be indexed with any of the following:
  230.  230  *
  231.  231  * 1, n, modulo, modulus
  232.  232  */
  233.  233 define('CRYPT_RSA_PUBLIC_FORMAT_RAW'3);
  234.  234 /**
  235.  235  * PKCS#1 formatted public key (raw)
  236.  236  *
  237.  237  * Used by File/X509.php
  238.  238  *
  239.  239  * Has the following header:
  240.  240  *
  241.  241  * -----BEGIN RSA PUBLIC KEY-----
  242.  242  *
  243.  243  * Analogous to ssh-keygen's pem format (as specified by -m)
  244.  244  */
  245.  245 define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1'4);
  246.  246 define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW'4);
  247.  247 /**
  248.  248  * XML formatted public key
  249.  249  */
  250.  250 define('CRYPT_RSA_PUBLIC_FORMAT_XML'5);
  251.  251 /**
  252.  252  * OpenSSH formatted public key
  253.  253  *
  254.  254  * Place in $HOME/.ssh/authorized_keys
  255.  255  */
  256.  256 define('CRYPT_RSA_PUBLIC_FORMAT_OPENSSH'6);
  257.  257 /**
  258.  258  * PKCS#1 formatted public key (encapsulated)
  259.  259  *
  260.  260  * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
  261.  261  *
  262.  262  * Has the following header:
  263.  263  *
  264.  264  * -----BEGIN PUBLIC KEY-----
  265.  265  *
  266.  266  * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
  267.  267  * is specific to private keys it's basically creating a DER-encoded wrapper
  268.  268  * for keys. This just extends that same concept to public keys (much like ssh-keygen)
  269.  269  */
  270.  270 define('CRYPT_RSA_PUBLIC_FORMAT_PKCS8'7);
  271.  271 /**#@-*/
  272.  272 
  273.  273 /**
  274.  274  * Pure-PHP PKCS#1 compliant implementation of RSA.
  275.  275  *
  276.  276  * @package Crypt_RSA
  277.  277  * @author  Jim Wigginton <terrafrost@php.net>
  278.  278  * @access  public
  279.  279  */
  280.  280 class Crypt_RSA
  281.  281 {
  282.  282     /**
  283.  283      * Precomputed Zero
  284.  284      *
  285.  285      * @var Math_BigInteger
  286.  286      * @access private
  287.  287      */
  288.  288     var $zero;
  289.  289 
  290.  290     /**
  291.  291      * Precomputed One
  292.  292      *
  293.  293      * @var Math_BigInteger
  294.  294      * @access private
  295.  295      */
  296.  296     var $one;
  297.  297 
  298.  298     /**
  299.  299      * Private Key Format
  300.  300      *
  301.  301      * @var int
  302.  302      * @access private
  303.  303      */
  304.  304     var $privateKeyFormat CRYPT_RSA_PRIVATE_FORMAT_PKCS1;
  305.  305 
  306.  306     /**
  307.  307      * Public Key Format
  308.  308      *
  309.  309      * @var int
  310.  310      * @access public
  311.  311      */
  312.  312     var $publicKeyFormat CRYPT_RSA_PUBLIC_FORMAT_PKCS8;
  313.  313 
  314.  314     /**
  315.  315      * Modulus (ie. n)
  316.  316      *
  317.  317      * @var Math_BigInteger
  318.  318      * @access private
  319.  319      */
  320.  320     var $modulus;
  321.  321 
  322.  322     /**
  323.  323      * Modulus length
  324.  324      *
  325.  325      * @var Math_BigInteger
  326.  326      * @access private
  327.  327      */
  328.  328     var $k;
  329.  329 
  330.  330     /**
  331.  331      * Exponent (ie. e or d)
  332.  332      *
  333.  333      * @var Math_BigInteger
  334.  334      * @access private
  335.  335      */
  336.  336     var $exponent;
  337.  337 
  338.  338     /**
  339.  339      * Primes for Chinese Remainder Theorem (ie. p and q)
  340.  340      *
  341.  341      * @var array
  342.  342      * @access private
  343.  343      */
  344.  344     var $primes;
  345.  345 
  346.  346     /**
  347.  347      * Exponents for Chinese Remainder Theorem (ie. dP and dQ)
  348.  348      *
  349.  349      * @var array
  350.  350      * @access private
  351.  351      */
  352.  352     var $exponents;
  353.  353 
  354.  354     /**
  355.  355      * Coefficients for Chinese Remainder Theorem (ie. qInv)
  356.  356      *
  357.  357      * @var array
  358.  358      * @access private
  359.  359      */
  360.  360     var $coefficients;
  361.  361 
  362.  362     /**
  363.  363      * Hash name
  364.  364      *
  365.  365      * @var string
  366.  366      * @access private
  367.  367      */
  368.  368     var $hashName;
  369.  369 
  370.  370     /**
  371.  371      * Hash function
  372.  372      *
  373.  373      * @var Crypt_Hash
  374.  374      * @access private
  375.  375      */
  376.  376     var $hash;
  377.  377 
  378.  378     /**
  379.  379      * Length of hash function output
  380.  380      *
  381.  381      * @var int
  382.  382      * @access private
  383.  383      */
  384.  384     var $hLen;
  385.  385 
  386.  386     /**
  387.  387      * Length of salt
  388.  388      *
  389.  389      * @var int
  390.  390      * @access private
  391.  391      */
  392.  392     var $sLen;
  393.  393 
  394.  394     /**
  395.  395      * Hash function for the Mask Generation Function
  396.  396      *
  397.  397      * @var Crypt_Hash
  398.  398      * @access private
  399.  399      */
  400.  400     var $mgfHash;
  401.  401 
  402.  402     /**
  403.  403      * Length of MGF hash function output
  404.  404      *
  405.  405      * @var int
  406.  406      * @access private
  407.  407      */
  408.  408     var $mgfHLen;
  409.  409 
  410.  410     /**
  411.  411      * Encryption mode
  412.  412      *
  413.  413      * @var int
  414.  414      * @access private
  415.  415      */
  416.  416     var $encryptionMode CRYPT_RSA_ENCRYPTION_OAEP;
  417.  417 
  418.  418     /**
  419.  419      * Signature mode
  420.  420      *
  421.  421      * @var int
  422.  422      * @access private
  423.  423      */
  424.  424     var $signatureMode CRYPT_RSA_SIGNATURE_PSS;
  425.  425 
  426.  426     /**
  427.  427      * Public Exponent
  428.  428      *
  429.  429      * @var mixed
  430.  430      * @access private
  431.  431      */
  432.  432     var $publicExponent false;
  433.  433 
  434.  434     /**
  435.  435      * Password
  436.  436      *
  437.  437      * @var string
  438.  438      * @access private
  439.  439      */
  440.  440     var $password false;
  441.  441 
  442.  442     /**
  443.  443      * Components
  444.  444      *
  445.  445      * For use with parsing XML formatted keys.  PHP's XML Parser functions use utilized - instead of PHP's DOM functions -
  446.  446      * because PHP's XML Parser functions work on PHP4 whereas PHP's DOM functions - although surperior - don't.
  447.  447      *
  448.  448      * @see self::_start_element_handler()
  449.  449      * @var array
  450.  450      * @access private
  451.  451      */
  452.  452     var $components = array();
  453.  453 
  454.  454     /**
  455.  455      * Current String
  456.  456      *
  457.  457      * For use with parsing XML formatted keys.
  458.  458      *
  459.  459      * @see self::_character_handler()
  460.  460      * @see self::_stop_element_handler()
  461.  461      * @var mixed
  462.  462      * @access private
  463.  463      */
  464.  464     var $current;
  465.  465 
  466.  466     /**
  467.  467      * OpenSSL configuration file name.
  468.  468      *
  469.  469      * Set to null to use system configuration file.
  470.  470      * @see self::createKey()
  471.  471      * @var mixed
  472.  472      * @Access public
  473.  473      */
  474.  474     var $configFile;
  475.  475 
  476.  476     /**
  477.  477      * Public key comment field.
  478.  478      *
  479.  479      * @var string
  480.  480      * @access private
  481.  481      */
  482.  482     var $comment 'phpseclib-generated-key';
  483.  483 
  484.  484     /**
  485.  485      * The constructor
  486.  486      *
  487.  487      * If you want to make use of the openssl extension, you'll need to set the mode manually, yourself.  The reason
  488.  488      * Crypt_RSA doesn't do it is because OpenSSL doesn't fail gracefully.  openssl_pkey_new(), in particular, requires
  489.  489      * openssl.cnf be present somewhere and, unfortunately, the only real way to find out is too late.
  490.  490      *
  491.  491      * @return Crypt_RSA
  492.  492      * @access public
  493.  493      */
  494.  494     function __construct()
  495.  495     {
  496.  496         if (!class_exists('Math_BigInteger')) {
  497.  497             include_once 'Math/BigInteger.php';
  498.  498         }
  499.  499 
  500.  500         $this->configFile CRYPT_RSA_OPENSSL_CONFIG;
  501.  501 
  502.  502         if (!defined('CRYPT_RSA_MODE')) {
  503.  503             switch (true) {
  504.  504                 // Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular,
  505.  505                 // Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger
  506.  506                 // can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either.
  507.  507                 case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'):
  508.  508                     define('CRYPT_RSA_MODE'CRYPT_RSA_MODE_INTERNAL);
  509.  509                     break;
  510.  510                 // openssl_pkey_get_details - which is used in the only place Crypt/RSA.php uses OpenSSL - was introduced in PHP 5.2.0
  511.  511                 case !function_exists('openssl_pkey_get_details'):
  512.  512                     define('CRYPT_RSA_MODE'CRYPT_RSA_MODE_INTERNAL);
  513.  513                     break;
  514.  514                 case extension_loaded('openssl') && version_compare(PHP_VERSION'4.2.0''>=') && file_exists($this->configFile):
  515.  515                     // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
  516.  516                     ob_start();
  517.  517                     @phpinfo();
  518.  518                     $content ob_get_contents();
  519.  519                     ob_end_clean();
  520.  520 
  521.  521                     preg_match_all('#OpenSSL (Header|Library) Version(.*)#im'$content$matches);
  522.  522 
  523.  523                     $versions = array();
  524.  524                     if (!empty($matches[1])) {
  525.  525                         for ($i 0$i count($matches[1]); $i++) {
  526.  526                             $fullVersion trim(str_replace('=>'''strip_tags($matches[2][$i])));
  527.  527 
  528.  528                             // Remove letter part in OpenSSL version
  529.  529                             if (!preg_match('/(\d+\.\d+\.\d+)/i'$fullVersion$m)) {
  530.  530                                 $versions[$matches[1][$i]] = $fullVersion;
  531.  531                             } else {
  532.  532                                 $versions[$matches[1][$i]] = $m[0];
  533.  533                             }
  534.  534                         }
  535.  535                     }
  536.  536 
  537.  537                     // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+
  538.  538                     switch (true) {
  539.  539                         case !isset($versions['Header']):
  540.  540                         case !isset($versions['Library']):
  541.  541                         case $versions['Header'] == $versions['Library']:
  542.  542                         case version_compare($versions['Header'], '1.0.0') >= && version_compare($versions['Library'], '1.0.0') >= 0:
  543.  543                             define('CRYPT_RSA_MODE'CRYPT_RSA_MODE_OPENSSL);
  544.  544                             break;
  545.  545                         default:
  546.  546                             define('CRYPT_RSA_MODE'CRYPT_RSA_MODE_INTERNAL);
  547.  547                             define('MATH_BIGINTEGER_OPENSSL_DISABLE'true);
  548.  548                     }
  549.  549                     break;
  550.  550                 default:
  551.  551                     define('CRYPT_RSA_MODE'CRYPT_RSA_MODE_INTERNAL);
  552.  552             }
  553.  553         }
  554.  554 
  555.  555         $this->zero = new Math_BigInteger();
  556.  556         $this->one = new Math_BigInteger(1);
  557.  557 
  558.  558         $this->hash = new Crypt_Hash('sha1');
  559.  559         $this->hLen $this->hash->getLength();
  560.  560         $this->hashName 'sha1';
  561.  561         $this->mgfHash = new Crypt_Hash('sha1');
  562.  562         $this->mgfHLen $this->mgfHash->getLength();
  563.  563     }
  564.  564 
  565.  565     /**
  566.  566      * PHP4 compatible Default Constructor.
  567.  567      *
  568.  568      * @see self::__construct()
  569.  569      * @access public
  570.  570      */
  571.  571     function Crypt_RSA()
  572.  572     {
  573.  573         $this->__construct();
  574.  574     }
  575.  575 
  576.  576     /**
  577.  577      * Create public / private key pair
  578.  578      *
  579.  579      * Returns an array with the following three elements:
  580.  580      *  - 'privatekey': The private key.
  581.  581      *  - 'publickey':  The public key.
  582.  582      *  - 'partialkey': A partially computed key (if the execution time exceeded $timeout).
  583.  583      *                  Will need to be passed back to Crypt_RSA::createKey() as the third parameter for further processing.
  584.  584      *
  585.  585      * @access public
  586.  586      * @param int $bits
  587.  587      * @param int $timeout
  588.  588      * @param Math_BigInteger $p
  589.  589      */
  590.  590     function createKey($bits 1024$timeout false$partial = array())
  591.  591     {
  592.  592         if (!defined('CRYPT_RSA_EXPONENT')) {
  593.  593             // http://en.wikipedia.org/wiki/65537_%28number%29
  594.  594             define('CRYPT_RSA_EXPONENT''65537');
  595.  595         }
  596.  596         // per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller
  597.  597         // than 256 bits. as a consequence if the key you're trying to create is 1024 bits and you've set CRYPT_RSA_SMALLEST_PRIME
  598.  598         // to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). at least if
  599.  599         // CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_INTERNAL. if CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_OPENSSL then
  600.  600         // CRYPT_RSA_SMALLEST_PRIME is ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key
  601.  601         // generation when there's a chance neither gmp nor OpenSSL are installed)
  602.  602         if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
  603.  603             define('CRYPT_RSA_SMALLEST_PRIME'4096);
  604.  604         }
  605.  605 
  606.  606         // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum
  607.  607         if (CRYPT_RSA_MODE == CRYPT_RSA_MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) {
  608.  608             $config = array();
  609.  609             if (isset($this->configFile)) {
  610.  610                 $config['config'] = $this->configFile;
  611.  611             }
  612.  612             $rsa openssl_pkey_new(array('private_key_bits' => $bits) + $config);
  613.  613             openssl_pkey_export($rsa$privatekeynull$config);
  614.  614             $publickey openssl_pkey_get_details($rsa);
  615.  615             $publickey $publickey['key'];
  616.  616 
  617.  617             $privatekey call_user_func_array(array($this'_convertPrivateKey'), array_values($this->_parseKey($privatekeyCRYPT_RSA_PRIVATE_FORMAT_PKCS1)));
  618.  618             $publickey call_user_func_array(array($this'_convertPublicKey'), array_values($this->_parseKey($publickeyCRYPT_RSA_PUBLIC_FORMAT_PKCS1)));
  619.  619 
  620.  620             // clear the buffer of error strings stemming from a minimalistic openssl.cnf
  621.  621             while (openssl_error_string() !== false) {
  622.  622             }
  623.  623 
  624.  624             return array(
  625.  625                 'privatekey' => $privatekey,
  626.  626                 'publickey' => $publickey,
  627.  627                 'partialkey' => false
  628.  628             );
  629.  629         }
  630.  630 
  631.  631         static $e;
  632.  632         if (!isset($e)) {
  633.  633             $e = new Math_BigInteger(CRYPT_RSA_EXPONENT);
  634.  634         }
  635.  635 
  636.  636         extract($this->_generateMinMax($bits));
  637.  637         $absoluteMin $min;
  638.  638         $temp $bits >> 1// divide by two to see how many bits P and Q would be
  639.  639         if ($temp CRYPT_RSA_SMALLEST_PRIME) {
  640.  640             $num_primes floor($bits CRYPT_RSA_SMALLEST_PRIME);
  641.  641             $temp CRYPT_RSA_SMALLEST_PRIME;
  642.  642         } else {
  643.  643             $num_primes 2;
  644.  644         }
  645.  645         extract($this->_generateMinMax($temp $bits $temp));
  646.  646         $finalMax $max;
  647.  647         extract($this->_generateMinMax($temp));
  648.  648 
  649.  649         $generator = new Math_BigInteger();
  650.  650 
  651.  651         $n $this->one->copy();
  652.  652         if (!empty($partial)) {
  653.  653             extract(unserialize($partial));
  654.  654         } else {
  655.  655             $exponents $coefficients $primes = array();
  656.  656             $lcm = array(
  657.  657                 'top' => $this->one->copy(),
  658.  658                 'bottom' => false
  659.  659             );
  660.  660         }
  661.  661 
  662.  662         $start time();
  663.  663         $i0 count($primes) + 1;
  664.  664 
  665.  665         do {
  666.  666             for ($i $i0$i <= $num_primes$i++) {
  667.  667                 if ($timeout !== false) {
  668.  668                     $timeout-= time() - $start;
  669.  669                     $start time();
  670.  670                     if ($timeout <= 0) {
  671.  671                         return array(
  672.  672                             'privatekey' => '',
  673.  673                             'publickey'  => '',
  674.  674                             'partialkey' => serialize(array(
  675.  675                                 'primes' => $primes,
  676.  676                                 'coefficients' => $coefficients,
  677.  677                                 'lcm' => $lcm,
  678.  678                                 'exponents' => $exponents
  679.  679                             ))
  680.  680                         );
  681.  681                     }
  682.  682                 }
  683.  683 
  684.  684                 if ($i == $num_primes) {
  685.  685                     list($min$temp) = $absoluteMin->divide($n);
  686.  686                     if (!$temp->equals($this->zero)) {
  687.  687                         $min $min->add($this->one); // ie. ceil()
  688.  688                     }
  689.  689                     $primes[$i] = $generator->randomPrime($min$finalMax$timeout);
  690.  690                 } else {
  691.  691                     $primes[$i] = $generator->randomPrime($min$max$timeout);
  692.  692                 }
  693.  693 
  694.  694                 if ($primes[$i] === false) { // if we've reached the timeout
  695.  695                     if (count($primes) > 1) {
  696.  696                         $partialkey '';
  697.  697                     } else {
  698.  698                         array_pop($primes);
  699.  699                         $partialkey serialize(array(
  700.  700                             'primes' => $primes,
  701.  701                             'coefficients' => $coefficients,
  702.  702                             'lcm' => $lcm,
  703.  703                             'exponents' => $exponents
  704.  704                         ));
  705.  705                     }
  706.  706 
  707.  707                     return array(
  708.  708                         'privatekey' => '',
  709.  709                         'publickey'  => '',
  710.  710                         'partialkey' => $partialkey
  711.  711                     );
  712.  712                 }
  713.  713 
  714.  714                 // the first coefficient is calculated differently from the rest
  715.  715                 // ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1])
  716.  716                 if ($i 2) {
  717.  717                     $coefficients[$i] = $n->modInverse($primes[$i]);
  718.  718                 }
  719.  719 
  720.  720                 $n $n->multiply($primes[$i]);
  721.  721 
  722.  722                 $temp $primes[$i]->subtract($this->one);
  723.  723 
  724.  724                 // textbook RSA implementations use Euler's totient function instead of the least common multiple.
  725.  725                 // see http://en.wikipedia.org/wiki/Euler%27s_totient_function
  726.  726                 $lcm['top'] = $lcm['top']->multiply($temp);
  727.  727                 $lcm['bottom'] = $lcm['bottom'] === false $temp $lcm['bottom']->gcd($temp);
  728.  728 
  729.  729                 $exponents[$i] = $e->modInverse($temp);
  730.  730             }
  731.  731 
  732.  732             list($temp) = $lcm['top']->divide($lcm['bottom']);
  733.  733             $gcd $temp->gcd($e);
  734.  734             $i0 1;
  735.  735         } while (!$gcd->equals($this->one));
  736.  736 
  737.  737         $d $e->modInverse($temp);
  738.  738 
  739.  739         $coefficients[2] = $primes[2]->modInverse($primes[1]);
  740.  740 
  741.  741         // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>:
  742.  742         // RSAPrivateKey ::= SEQUENCE {
  743.  743         //     version           Version,
  744.  744         //     modulus           INTEGER,  -- n
  745.  745         //     publicExponent    INTEGER,  -- e
  746.  746         //     privateExponent   INTEGER,  -- d
  747.  747         //     prime1            INTEGER,  -- p
  748.  748         //     prime2            INTEGER,  -- q
  749.  749         //     exponent1         INTEGER,  -- d mod (p-1)
  750.  750         //     exponent2         INTEGER,  -- d mod (q-1)
  751.  751         //     coefficient       INTEGER,  -- (inverse of q) mod p
  752.  752         //     otherPrimeInfos   OtherPrimeInfos OPTIONAL
  753.  753         // }
  754.  754 
  755.  755         return array(
  756.  756             'privatekey' => $this->_convertPrivateKey($n$e$d$primes$exponents$coefficients),
  757.  757             'publickey'  => $this->_convertPublicKey($n$e),
  758.  758             'partialkey' => false
  759.  759         );
  760.  760     }
  761.  761 
  762.  762     /**
  763.  763      * Convert a private key to the appropriate format.
  764.  764      *
  765.  765      * @access private
  766.  766      * @see self::setPrivateKeyFormat()
  767.  767      * @param string $RSAPrivateKey
  768.  768      * @return string
  769.  769      */
  770.  770     function _convertPrivateKey($n$e$d$primes$exponents$coefficients)
  771.  771     {
  772.  772         $signed $this->privateKeyFormat != CRYPT_RSA_PRIVATE_FORMAT_XML;
  773.  773         $num_primes count($primes);
  774.  774         $raw = array(
  775.  775             'version' => $num_primes == chr(0) : chr(1), // two-prime vs. multi
  776.  776             'modulus' => $n->toBytes($signed),
  777.  777             'publicExponent' => $e->toBytes($signed),
  778.  778             'privateExponent' => $d->toBytes($signed),
  779.  779             'prime1' => $primes[1]->toBytes($signed),
  780.  780             'prime2' => $primes[2]->toBytes($signed),
  781.  781             'exponent1' => $exponents[1]->toBytes($signed),
  782.  782             'exponent2' => $exponents[2]->toBytes($signed),
  783.  783             'coefficient' => $coefficients[2]->toBytes($signed)
  784.  784         );
  785.  785 
  786.  786         // if the format in question does not support multi-prime rsa and multi-prime rsa was used,
  787.  787         // call _convertPublicKey() instead.
  788.  788         switch ($this->privateKeyFormat) {
  789.  789             case CRYPT_RSA_PRIVATE_FORMAT_XML:
  790.  790                 if ($num_primes != 2) {
  791.  791                     return false;
  792.  792                 }
  793.  793                 return "<RSAKeyValue>\r\n" .
  794.  794                        '  <Modulus>' base64_encode($raw['modulus']) . "</Modulus>\r\n" .
  795.  795                        '  <Exponent>' base64_encode($raw['publicExponent']) . "</Exponent>\r\n" .
  796.  796                        '  <P>' base64_encode($raw['prime1']) . "</P>\r\n" .
  797.  797                        '  <Q>' base64_encode($raw['prime2']) . "</Q>\r\n" .
  798.  798                        '  <DP>' base64_encode($raw['exponent1']) . "</DP>\r\n" .
  799.  799                        '  <DQ>' base64_encode($raw['exponent2']) . "</DQ>\r\n" .
  800.  800                        '  <InverseQ>' base64_encode($raw['coefficient']) . "</InverseQ>\r\n" .
  801.  801                        '  <D>' base64_encode($raw['privateExponent']) . "</D>\r\n" .
  802.  802                        '</RSAKeyValue>';
  803.  803                 break;
  804.  804             case CRYPT_RSA_PRIVATE_FORMAT_PUTTY:
  805.  805                 if ($num_primes != 2) {
  806.  806                     return false;
  807.  807                 }
  808.  808                 $key "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: ";
  809.  809                 $encryption = (!empty($this->password) || is_string($this->password)) ? 'aes256-cbc' 'none';
  810.  810                 $key.= $encryption;
  811.  811                 $key.= "\r\nComment: " $this->comment "\r\n";
  812.  812                 $public pack(
  813.  813                     'Na*Na*Na*',
  814.  814                     strlen('ssh-rsa'),
  815.  815                     'ssh-rsa',
  816.  816                     strlen($raw['publicExponent']),
  817.  817                     $raw['publicExponent'],
  818.  818                     strlen($raw['modulus']),
  819.  819                     $raw['modulus']
  820.  820                 );
  821.  821                 $source pack(
  822.  822                     'Na*Na*Na*Na*',
  823.  823                     strlen('ssh-rsa'),
  824.  824                     'ssh-rsa',
  825.  825                     strlen($encryption),
  826.  826                     $encryption,
  827.  827                     strlen($this->comment),
  828.  828                     $this->comment,
  829.  829                     strlen($public),
  830.  830                     $public
  831.  831                 );
  832.  832                 $public base64_encode($public);
  833.  833                 $key.= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n";
  834.  834                 $key.= chunk_split($public64);
  835.  835                 $private pack(
  836.  836                     'Na*Na*Na*Na*',
  837.  837                     strlen($raw['privateExponent']),
  838.  838                     $raw['privateExponent'],
  839.  839                     strlen($raw['prime1']),
  840.  840                     $raw['prime1'],
  841.  841                     strlen($raw['prime2']),
  842.  842                     $raw['prime2'],
  843.  843                     strlen($raw['coefficient']),
  844.  844                     $raw['coefficient']
  845.  845                 );
  846.  846                 if (empty($this->password) && !is_string($this->password)) {
  847.  847                     $source.= pack('Na*'strlen($private), $private);
  848.  848                     $hashkey 'putty-private-key-file-mac-key';
  849.  849                 } else {
  850.  850                     $private.= crypt_random_string(16 - (strlen($private) & 15));
  851.  851                     $source.= pack('Na*'strlen($private), $private);
  852.  852                     if (!class_exists('Crypt_AES')) {
  853.  853                         include_once 'Crypt/AES.php';
  854.  854                     }
  855.  855                     $sequence 0;
  856.  856                     $symkey '';
  857.  857                     while (strlen($symkey) < 32) {
  858.  858                         $temp pack('Na*'$sequence++, $this->password);
  859.  859                         $symkey.= pack('H*'sha1($temp));
  860.  860                     }
  861.  861                     $symkey substr($symkey032);
  862.  862                     $crypto = new Crypt_AES();
  863.  863 
  864.  864                     $crypto->setKey($symkey);
  865.  865                     $crypto->disablePadding();
  866.  866                     $private $crypto->encrypt($private);
  867.  867                     $hashkey 'putty-private-key-file-mac-key' $this->password;
  868.  868                 }
  869.  869 
  870.  870                 $private base64_encode($private);
  871.  871                 $key.= 'Private-Lines: ' . ((strlen($private) + 63) >> 6) . "\r\n";
  872.  872                 $key.= chunk_split($private64);
  873.  873                 if (!class_exists('Crypt_Hash')) {
  874.  874                     include_once 'Crypt/Hash.php';
  875.  875                 }
  876.  876                 $hash = new Crypt_Hash('sha1');
  877.  877                 $hash->setKey(pack('H*'sha1($hashkey)));
  878.  878                 $key.= 'Private-MAC: ' bin2hex($hash->hash($source)) . "\r\n";
  879.  879 
  880.  880                 return $key;
  881.  881             default: // eg. CRYPT_RSA_PRIVATE_FORMAT_PKCS1
  882.  882                 $components = array();
  883.  883                 foreach ($raw as $name => $value) {
  884.  884                     $components[$name] = pack('Ca*a*'CRYPT_RSA_ASN1_INTEGER$this->_encodeLength(strlen($value)), $value);
  885.  885                 }
  886.  886 
  887.  887                 $RSAPrivateKey implode(''$components);
  888.  888 
  889.  889                 if ($num_primes 2) {
  890.  890                     $OtherPrimeInfos '';
  891.  891                     for ($i 3$i <= $num_primes$i++) {
  892.  892                         // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
  893.  893                         //
  894.  894                         // OtherPrimeInfo ::= SEQUENCE {
  895.  895                         //     prime             INTEGER,  -- ri
  896.  896                         //     exponent          INTEGER,  -- di
  897.  897                         //     coefficient       INTEGER   -- ti
  898.  898                         // }
  899.  899                         $OtherPrimeInfo pack('Ca*a*'CRYPT_RSA_ASN1_INTEGER$this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true));
  900.  900                         $OtherPrimeInfo.= pack('Ca*a*'CRYPT_RSA_ASN1_INTEGER$this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true));
  901.  901                         $OtherPrimeInfo.= pack('Ca*a*'CRYPT_RSA_ASN1_INTEGER$this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true));
  902.  902                         $OtherPrimeInfos.= pack('Ca*a*'CRYPT_RSA_ASN1_SEQUENCE$this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo);
  903.  903                     }
  904.  904                     $RSAPrivateKey.= pack('Ca*a*'CRYPT_RSA_ASN1_SEQUENCE$this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos);
  905.  905                 }
  906.  906 
  907.  907                 $RSAPrivateKey pack('Ca*a*'CRYPT_RSA_ASN1_SEQUENCE$this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
  908.  908 
  909.  909                 if ($this->privateKeyFormat == CRYPT_RSA_PRIVATE_FORMAT_PKCS8) {
  910.  910                     $rsaOID pack('H*''300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
  911.  911                     $RSAPrivateKey pack(
  912.  912                         'Ca*a*Ca*a*',
  913.  913                         CRYPT_RSA_ASN1_INTEGER,
  914.  914                         "\01\00",
  915.  915                         $rsaOID,
  916.  916                         4,
  917.  917                         $this->_encodeLength(strlen($RSAPrivateKey)),
  918.  918                         $RSAPrivateKey
  919.  919                     );
  920.  920                     $RSAPrivateKey pack('Ca*a*'CRYPT_RSA_ASN1_SEQUENCE$this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
  921.  921                     if (!empty($this->password) || is_string($this->password)) {
  922.  922                         $salt crypt_random_string(8);
  923.  923                         $iterationCount 2048;
  924.  924 
  925.  925                         if (!class_exists('Crypt_DES')) {
  926.  926                             include_once 'Crypt/DES.php';
  927.  927                         }
  928.  928                         $crypto = new Crypt_DES();
  929.  929                         $crypto->setPassword($this->password'pbkdf1''md5'$salt$iterationCount);
  930.  930                         $RSAPrivateKey $crypto->encrypt($RSAPrivateKey);
  931.  931 
  932.  932                         $parameters pack(
  933.  933                             'Ca*a*Ca*N',
  934.  934                             CRYPT_RSA_ASN1_OCTETSTRING,
  935.  935                             $this->_encodeLength(strlen($salt)),
  936.  936                             $salt,
  937.  937                             CRYPT_RSA_ASN1_INTEGER,
  938.  938                             $this->_encodeLength(4),
  939.  939                             $iterationCount
  940.  940                         );
  941.  941                         $pbeWithMD5AndDES_CBC "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03";
  942.  942 
  943.  943                         $encryptionAlgorithm pack(
  944.  944                             'Ca*a*Ca*a*',
  945.  945                             CRYPT_RSA_ASN1_OBJECT,
  946.  946                             $this->_encodeLength(strlen($pbeWithMD5AndDES_CBC)),
  947.  947                             $pbeWithMD5AndDES_CBC,
  948.  948                             CRYPT_RSA_ASN1_SEQUENCE,
  949.  949                             $this->_encodeLength(strlen($parameters)),
  950.  950                             $parameters
  951.  951                         );
  952.  952 
  953.  953                         $RSAPrivateKey pack(
  954.  954                             'Ca*a*Ca*a*',
  955.  955                             CRYPT_RSA_ASN1_SEQUENCE,
  956.  956                             $this->_encodeLength(strlen($encryptionAlgorithm)),
  957.  957                             $encryptionAlgorithm,
  958.  958                             CRYPT_RSA_ASN1_OCTETSTRING,
  959.  959                             $this->_encodeLength(strlen($RSAPrivateKey)),
  960.  960                             $RSAPrivateKey
  961.  961                         );
  962.  962 
  963.  963                         $RSAPrivateKey pack('Ca*a*'CRYPT_RSA_ASN1_SEQUENCE$this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
  964.  964 
  965.  965                         $RSAPrivateKey "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" .
  966.  966                                          chunk_split(base64_encode($RSAPrivateKey), 64) .
  967.  967                                          '-----END ENCRYPTED PRIVATE KEY-----';
  968.  968                     } else {
  969.  969                         $RSAPrivateKey "-----BEGIN PRIVATE KEY-----\r\n" .
  970.  970                                          chunk_split(base64_encode($RSAPrivateKey), 64) .
  971.  971                                          '-----END PRIVATE KEY-----';
  972.  972                     }
  973.  973                     return $RSAPrivateKey;
  974.  974                 }
  975.  975 
  976.  976                 if (!empty($this->password) || is_string($this->password)) {
  977.  977                     $iv crypt_random_string(8);
  978.  978                     $symkey pack('H*'md5($this->password $iv)); // symkey is short for symmetric key
  979.  979                     $symkey.= substr(pack('H*'md5($symkey $this->password $iv)), 08);
  980.  980                     if (!class_exists('Crypt_TripleDES')) {
  981.  981                         include_once 'Crypt/TripleDES.php';
  982.  982                     }
  983.  983                     $des = new Crypt_TripleDES();
  984.  984                     $des->setKey($symkey);
  985.  985                     $des->setIV($iv);
  986.  986                     $iv strtoupper(bin2hex($iv));
  987.  987                     $RSAPrivateKey "-----BEGIN RSA PRIVATE KEY-----\r\n" .
  988.  988                                      "Proc-Type: 4,ENCRYPTED\r\n" .
  989.  989                                      "DEK-Info: DES-EDE3-CBC,$iv\r\n" .
  990.  990                                      "\r\n" .
  991.  991                                      chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) .
  992.  992                                      '-----END RSA PRIVATE KEY-----';
  993.  993                 } else {
  994.  994                     $RSAPrivateKey "-----BEGIN RSA PRIVATE KEY-----\r\n" .
  995.  995                                      chunk_split(base64_encode($RSAPrivateKey), 64) .
  996.  996                                      '-----END RSA PRIVATE KEY-----';
  997.  997                 }
  998.  998 
  999.  999                 return $RSAPrivateKey;
  1000. 1000         }
  1001. 1001     }
  1002. 1002 
  1003. 1003     /**
  1004. 1004      * Convert a public key to the appropriate format
  1005. 1005      *
  1006. 1006      * @access private
  1007. 1007      * @see self::setPublicKeyFormat()
  1008. 1008      * @param string $RSAPrivateKey
  1009. 1009      * @return string
  1010. 1010      */
  1011. 1011     function _convertPublicKey($n$e)
  1012. 1012     {
  1013. 1013         $signed $this->publicKeyFormat != CRYPT_RSA_PUBLIC_FORMAT_XML;
  1014. 1014 
  1015. 1015         $modulus $n->toBytes($signed);
  1016. 1016         $publicExponent $e->toBytes($signed);
  1017. 1017 
  1018. 1018         switch ($this->publicKeyFormat) {
  1019. 1019             case CRYPT_RSA_PUBLIC_FORMAT_RAW:
  1020. 1020                 return array('e' => $e->copy(), 'n' => $n->copy());
  1021. 1021             case CRYPT_RSA_PUBLIC_FORMAT_XML:
  1022. 1022                 return "<RSAKeyValue>\r\n" .
  1023. 1023                        '  <Modulus>' base64_encode($modulus) . "</Modulus>\r\n" .
  1024. 1024                        '  <Exponent>' base64_encode($publicExponent) . "</Exponent>\r\n" .
  1025. 1025                        '</RSAKeyValue>';
  1026. 1026                 break;
  1027. 1027             case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
  1028. 1028                 // from <http://tools.ietf.org/html/rfc4253#page-15>:
  1029. 1029                 // string    "ssh-rsa"
  1030. 1030                 // mpint     e
  1031. 1031                 // mpint     n
  1032. 1032                 $RSAPublicKey pack('Na*Na*Na*'strlen('ssh-rsa'), 'ssh-rsa'strlen($publicExponent), $publicExponentstrlen($modulus), $modulus);
  1033. 1033                 $RSAPublicKey 'ssh-rsa ' base64_encode($RSAPublicKey) . ' ' $this->comment;
  1034. 1034 
  1035. 1035                 return $RSAPublicKey;
  1036. 1036             default: // eg. CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW or CRYPT_RSA_PUBLIC_FORMAT_PKCS1
  1037. 1037                 // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
  1038. 1038                 // RSAPublicKey ::= SEQUENCE {
  1039. 1039                 //     modulus           INTEGER,  -- n
  1040. 1040                 //     publicExponent    INTEGER   -- e
  1041. 1041                 // }
  1042. 1042                 $components = array(
  1043. 1043                     'modulus' => pack('Ca*a*'CRYPT_RSA_ASN1_INTEGER$this->_encodeLength(strlen($modulus)), $modulus),
  1044. 1044                     'publicExponent' => pack('Ca*a*'CRYPT_RSA_ASN1_INTEGER$this->_encodeLength(strlen($publicExponent)), $publicExponent)
  1045. 1045                 );
  1046. 1046 
  1047. 1047                 $RSAPublicKey pack(
  1048. 1048                     'Ca*a*a*',
  1049. 1049                     CRYPT_RSA_ASN1_SEQUENCE,
  1050. 1050                     $this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
  1051. 1051                     $components['modulus'],
  1052. 1052                     $components['publicExponent']
  1053. 1053                 );
  1054. 1054 
  1055. 1055                 if ($this->publicKeyFormat == CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW) {
  1056. 1056                     $RSAPublicKey "-----BEGIN RSA PUBLIC KEY-----\r\n" .
  1057. 1057                                     chunk_split(base64_encode($RSAPublicKey), 64) .
  1058. 1058                                     '-----END RSA PUBLIC KEY-----';
  1059. 1059                 } else {
  1060. 1060                     // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
  1061. 1061                     $rsaOID pack('H*''300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
  1062. 1062                     $RSAPublicKey chr(0) . $RSAPublicKey;
  1063. 1063                     $RSAPublicKey chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
  1064. 1064 
  1065. 1065                     $RSAPublicKey pack(
  1066. 1066                         'Ca*a*',
  1067. 1067                         CRYPT_RSA_ASN1_SEQUENCE,
  1068. 1068                         $this->_encodeLength(strlen($rsaOID $RSAPublicKey)),
  1069. 1069                         $rsaOID $RSAPublicKey
  1070. 1070                     );
  1071. 1071 
  1072. 1072                     $RSAPublicKey "-----BEGIN PUBLIC KEY-----\r\n" .
  1073. 1073                                      chunk_split(base64_encode($RSAPublicKey), 64) .
  1074. 1074                                      '-----END PUBLIC KEY-----';
  1075. 1075                 }
  1076. 1076 
  1077. 1077                 return $RSAPublicKey;
  1078. 1078         }
  1079. 1079     }
  1080. 1080 
  1081. 1081     /**
  1082. 1082      * Break a public or private key down into its constituant components
  1083. 1083      *
  1084. 1084      * @access private
  1085. 1085      * @see self::_convertPublicKey()
  1086. 1086      * @see self::_convertPrivateKey()
  1087. 1087      * @param string $key
  1088. 1088      * @param int $type
  1089. 1089      * @return array
  1090. 1090      */
  1091. 1091     function _parseKey($key$type)
  1092. 1092     {
  1093. 1093         if ($type != CRYPT_RSA_PUBLIC_FORMAT_RAW && !is_string($key)) {
  1094. 1094             return false;
  1095. 1095         }
  1096. 1096 
  1097. 1097         switch ($type) {
  1098. 1098             case CRYPT_RSA_PUBLIC_FORMAT_RAW:
  1099. 1099                 if (!is_array($key)) {
  1100. 1100                     return false;
  1101. 1101                 }
  1102. 1102                 $components = array();
  1103. 1103                 switch (true) {
  1104. 1104                     case isset($key['e']):
  1105. 1105                         $components['publicExponent'] = $key['e']->copy();
  1106. 1106                         break;
  1107. 1107                     case isset($key['exponent']):
  1108. 1108                         $components['publicExponent'] = $key['exponent']->copy();
  1109. 1109                         break;
  1110. 1110                     case isset($key['publicExponent']):
  1111. 1111                         $components['publicExponent'] = $key['publicExponent']->copy();
  1112. 1112                         break;
  1113. 1113                     case isset($key[0]):
  1114. 1114                         $components['publicExponent'] = $key[0]->copy();
  1115. 1115                 }
  1116. 1116                 switch (true) {
  1117. 1117                     case isset($key['n']):
  1118. 1118                         $components['modulus'] = $key['n']->copy();
  1119. 1119                         break;
  1120. 1120                     case isset($key['modulo']):
  1121. 1121                         $components['modulus'] = $key['modulo']->copy();
  1122. 1122                         break;
  1123. 1123                     case isset($key['modulus']):
  1124. 1124                         $components['modulus'] = $key['modulus']->copy();
  1125. 1125                         break;
  1126. 1126                     case isset($key[1]):
  1127. 1127                         $components['modulus'] = $key[1]->copy();
  1128. 1128                 }
  1129. 1129                 return isset($components['modulus']) && isset($components['publicExponent']) ? $components false;
  1130. 1130             case CRYPT_RSA_PRIVATE_FORMAT_PKCS1:
  1131. 1131             case CRYPT_RSA_PRIVATE_FORMAT_PKCS8:
  1132. 1132             case CRYPT_RSA_PUBLIC_FORMAT_PKCS1:
  1133. 1133                 /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
  1134. 1134                    "outside the scope" of PKCS#1.  PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
  1135. 1135                    protect private keys, however, that's not what OpenSSL* does.  OpenSSL protects private keys by adding
  1136. 1136                    two new "fields" to the key - DEK-Info and Proc-Type.  These fields are discussed here:
  1137. 1137 
  1138. 1138                    http://tools.ietf.org/html/rfc1421#section-4.6.1.1
  1139. 1139                    http://tools.ietf.org/html/rfc1421#section-4.6.1.3
  1140. 1140 
  1141. 1141                    DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
  1142. 1142                    DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
  1143. 1143                    function.  As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
  1144. 1144                    own implementation.  ie. the implementation *is* the standard and any bugs that may exist in that
  1145. 1145                    implementation are part of the standard, as well.
  1146. 1146 
  1147. 1147                    * OpenSSL is the de facto standard.  It's utilized by OpenSSH and other projects */
  1148. 1148                 if (preg_match('#DEK-Info: (.+),(.+)#'$key$matches)) {
  1149. 1149                     $iv pack('H*'trim($matches[2]));
  1150. 1150                     $symkey pack('H*'md5($this->password substr($iv08))); // symkey is short for symmetric key
  1151. 1151                     $symkey.= pack('H*'md5($symkey $this->password substr($iv08)));
  1152. 1152                     // remove the Proc-Type / DEK-Info sections as they're no longer needed
  1153. 1153                     $key preg_replace('#^(?:Proc-Type|DEK-Info): .*#m'''$key);
  1154. 1154                     $ciphertext $this->_extractBER($key);
  1155. 1155                     if ($ciphertext === false) {
  1156. 1156                         $ciphertext $key;
  1157. 1157                     }
  1158. 1158                     switch ($matches[1]) {
  1159. 1159                         case 'AES-256-CBC':
  1160. 1160                             if (!class_exists('Crypt_AES')) {
  1161. 1161                                 include_once 'Crypt/AES.php';
  1162. 1162                             }
  1163. 1163                             $crypto = new Crypt_AES();
  1164. 1164                             break;
  1165. 1165                         case 'AES-128-CBC':
  1166. 1166                             if (!class_exists('Crypt_AES')) {
  1167. 1167                                 include_once 'Crypt/AES.php';
  1168. 1168                             }
  1169. 1169                             $symkey substr($symkey016);
  1170. 1170                             $crypto = new Crypt_AES();
  1171. 1171                             break;
  1172. 1172                         case 'DES-EDE3-CFB':
  1173. 1173                             if (!class_exists('Crypt_TripleDES')) {
  1174. 1174                                 include_once 'Crypt/TripleDES.php';
  1175. 1175                             }
  1176. 1176                             $crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CFB);
  1177. 1177                             break;
  1178. 1178                         case 'DES-EDE3-CBC':
  1179. 1179                             if (!class_exists('Crypt_TripleDES')) {
  1180. 1180                                 include_once 'Crypt/TripleDES.php';
  1181. 1181                             }
  1182. 1182                             $symkey substr($symkey024);
  1183. 1183                             $crypto = new Crypt_TripleDES();
  1184. 1184                             break;
  1185. 1185                         case 'DES-CBC':
  1186. 1186                             if (!class_exists('Crypt_DES')) {
  1187. 1187                                 include_once 'Crypt/DES.php';
  1188. 1188                             }
  1189. 1189                             $crypto = new Crypt_DES();
  1190. 1190                             break;
  1191. 1191                         default:
  1192. 1192                             return false;
  1193. 1193                     }
  1194. 1194                     $crypto->setKey($symkey);
  1195. 1195                     $crypto->setIV($iv);
  1196. 1196                     $decoded $crypto->decrypt($ciphertext);
  1197. 1197                 } else {
  1198. 1198                     $decoded $this->_extractBER($key);
  1199. 1199                 }
  1200. 1200 
  1201. 1201                 if ($decoded !== false) {
  1202. 1202                     $key $decoded;
  1203. 1203                 }
  1204. 1204 
  1205. 1205                 $components = array();
  1206. 1206 
  1207. 1207                 if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
  1208. 1208                     return false;
  1209. 1209                 }
  1210. 1210                 if ($this->_decodeLength($key) != strlen($key)) {
  1211. 1211                     return false;
  1212. 1212                 }
  1213. 1213 
  1214. 1214                 $tag ord($this->_string_shift($key));
  1215. 1215                 /* intended for keys for which OpenSSL's asn1parse returns the following:
  1216. 1216 
  1217. 1217                     0:d=0  hl=4 l= 631 cons: SEQUENCE
  1218. 1218                     4:d=1  hl=2 l=   1 prim:  INTEGER           :00
  1219. 1219                     7:d=1  hl=2 l=  13 cons:  SEQUENCE
  1220. 1220                     9:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
  1221. 1221                    20:d=2  hl=2 l=   0 prim:   NULL
  1222. 1222                    22:d=1  hl=4 l= 609 prim:  OCTET STRING
  1223. 1223 
  1224. 1224                    ie. PKCS8 keys*/
  1225. 1225 
  1226. 1226                 if ($tag == CRYPT_RSA_ASN1_INTEGER && substr($key03) == "\x01\x00\x30") {
  1227. 1227                     $this->_string_shift($key3);
  1228. 1228                     $tag CRYPT_RSA_ASN1_SEQUENCE;
  1229. 1229                 }
  1230. 1230 
  1231. 1231                 if ($tag == CRYPT_RSA_ASN1_SEQUENCE) {
  1232. 1232                     $temp $this->_string_shift($key$this->_decodeLength($key));
  1233. 1233                     if (ord($this->_string_shift($temp)) != CRYPT_RSA_ASN1_OBJECT) {
  1234. 1234                         return false;
  1235. 1235                     }
  1236. 1236                     $length $this->_decodeLength($temp);
  1237. 1237                     switch ($this->_string_shift($temp$length)) {
  1238. 1238                         case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01"// rsaEncryption
  1239. 1239                             break;
  1240. 1240                         case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03"// pbeWithMD5AndDES-CBC
  1241. 1241                             /*
  1242. 1242                                PBEParameter ::= SEQUENCE {
  1243. 1243                                    salt OCTET STRING (SIZE(8)),
  1244. 1244                                    iterationCount INTEGER }
  1245. 1245                             */
  1246. 1246                             if (ord($this->_string_shift($temp)) != CRYPT_RSA_ASN1_SEQUENCE) {
  1247. 1247                                 return false;
  1248. 1248                             }
  1249. 1249                             if ($this->_decodeLength($temp) != strlen($temp)) {
  1250. 1250                                 return false;
  1251. 1251                             }
  1252. 1252                             $this->_string_shift($temp); // assume it's an octet string
  1253. 1253                             $salt $this->_string_shift($temp$this->_decodeLength($temp));
  1254. 1254                             if (ord($this->_string_shift($temp)) != CRYPT_RSA_ASN1_INTEGER) {
  1255. 1255                                 return false;
  1256. 1256                             }
  1257. 1257                             $this->_decodeLength($temp);
  1258. 1258                             list(, $iterationCount) = unpack('N'str_pad($temp4chr(0), STR_PAD_LEFT));
  1259. 1259                             $this->_string_shift($key); // assume it's an octet string
  1260. 1260                             $length $this->_decodeLength($key);
  1261. 1261                             if (strlen($key) != $length) {
  1262. 1262                                 return false;
  1263. 1263                             }
  1264. 1264 
  1265. 1265                             if (!class_exists('Crypt_DES')) {
  1266. 1266                                 include_once 'Crypt/DES.php';
  1267. 1267                             }
  1268. 1268                             $crypto = new Crypt_DES();
  1269. 1269                             $crypto->setPassword($this->password'pbkdf1''md5'$salt$iterationCount);
  1270. 1270                             $key $crypto->decrypt($key);
  1271. 1271                             if ($key === false) {
  1272. 1272                                 return false;
  1273. 1273                             }
  1274. 1274                             return $this->_parseKey($keyCRYPT_RSA_PRIVATE_FORMAT_PKCS1);
  1275. 1275                         default:
  1276. 1276                             return false;
  1277. 1277                     }
  1278. 1278                     /* intended for keys for which OpenSSL's asn1parse returns the following:
  1279. 1279 
  1280. 1280                         0:d=0  hl=4 l= 290 cons: SEQUENCE
  1281. 1281                         4:d=1  hl=2 l=  13 cons:  SEQUENCE
  1282. 1282                         6:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
  1283. 1283                        17:d=2  hl=2 l=   0 prim:   NULL
  1284. 1284                        19:d=1  hl=4 l= 271 prim:  BIT STRING */
  1285. 1285                     $tag ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag
  1286. 1286                     $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length
  1287. 1287                     // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of
  1288. 1288                     //  unused bits in the final subsequent octet. The number shall be in the range zero to seven."
  1289. 1289                     //  -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2)
  1290. 1290                     if ($tag == CRYPT_RSA_ASN1_BITSTRING) {
  1291. 1291                         $this->_string_shift($key);
  1292. 1292                     }
  1293. 1293                     if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
  1294. 1294                         return false;
  1295. 1295                     }
  1296. 1296                     if ($this->_decodeLength($key) != strlen($key)) {
  1297. 1297                         return false;
  1298. 1298                     }
  1299. 1299                     $tag ord($this->_string_shift($key));
  1300. 1300                 }
  1301. 1301                 if ($tag != CRYPT_RSA_ASN1_INTEGER) {
  1302. 1302                     return false;
  1303. 1303                 }
  1304. 1304 
  1305. 1305                 $length $this->_decodeLength($key);
  1306. 1306                 $temp $this->_string_shift($key$length);
  1307. 1307                 if (strlen($temp) != || ord($temp) > 2) {
  1308. 1308                     $components['modulus'] = new Math_BigInteger($temp256);
  1309. 1309                     $this->_string_shift($key); // skip over CRYPT_RSA_ASN1_INTEGER
  1310. 1310                     $length $this->_decodeLength($key);
  1311. 1311                     $components[$type == CRYPT_RSA_PUBLIC_FORMAT_PKCS1 'publicExponent' 'privateExponent'] = new Math_BigInteger($this->_string_shift($key$length), 256);
  1312. 1312 
  1313. 1313                     return $components;
  1314. 1314                 }
  1315. 1315                 if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_INTEGER) {
  1316. 1316                     return false;
  1317. 1317                 }
  1318. 1318                 $length $this->_decodeLength($key);
  1319. 1319                 $components['modulus'] = new Math_BigInteger($this->_string_shift($key$length), 256);
  1320. 1320                 $this->_string_shift($key);
  1321. 1321                 $length $this->_decodeLength($key);
  1322. 1322                 $components['publicExponent'] = new Math_BigInteger($this->_string_shift($key$length), 256);
  1323. 1323                 $this->_string_shift($key);
  1324. 1324                 $length $this->_decodeLength($key);
  1325. 1325                 $components['privateExponent'] = new Math_BigInteger($this->_string_shift($key$length), 256);
  1326. 1326                 $this->_string_shift($key);
  1327. 1327                 $length $this->_decodeLength($key);
  1328. 1328                 $components['primes'] = array(=> new Math_BigInteger($this->_string_shift($key$length), 256));
  1329. 1329                 $this->_string_shift($key);
  1330. 1330                 $length $this->_decodeLength($key);
  1331. 1331                 $components['primes'][] = new Math_BigInteger($this->_string_shift($key$length), 256);
  1332. 1332                 $this->_string_shift($key);
  1333. 1333                 $length $this->_decodeLength($key);
  1334. 1334                 $components['exponents'] = array(=> new Math_BigInteger($this->_string_shift($key$length), 256));
  1335. 1335                 $this->_string_shift($key);
  1336. 1336                 $length $this->_decodeLength($key);
  1337. 1337                 $components['exponents'][] = new Math_BigInteger($this->_string_shift($key$length), 256);
  1338. 1338                 $this->_string_shift($key);
  1339. 1339                 $length $this->_decodeLength($key);
  1340. 1340                 $components['coefficients'] = array(=> new Math_BigInteger($this->_string_shift($key$length), 256));
  1341. 1341 
  1342. 1342                 if (!empty($key)) {
  1343. 1343                     if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
  1344. 1344                         return false;
  1345. 1345                     }
  1346. 1346                     $this->_decodeLength($key);
  1347. 1347                     while (!empty($key)) {
  1348. 1348                         if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
  1349. 1349                             return false;
  1350. 1350                         }
  1351. 1351                         $this->_decodeLength($key);
  1352. 1352                         $key substr($key1);
  1353. 1353                         $length $this->_decodeLength($key);
  1354. 1354                         $components['primes'][] = new Math_BigInteger($this->_string_shift($key$length), 256);
  1355. 1355                         $this->_string_shift($key);
  1356. 1356                         $length $this->_decodeLength($key);
  1357. 1357                         $components['exponents'][] = new Math_BigInteger($this->_string_shift($key$length), 256);
  1358. 1358                         $this->_string_shift($key);
  1359. 1359                         $length $this->_decodeLength($key);
  1360. 1360                         $components['coefficients'][] = new Math_BigInteger($this->_string_shift($key$length), 256);
  1361. 1361                     }
  1362. 1362                 }
  1363. 1363 
  1364. 1364                 return $components;
  1365. 1365             case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
  1366. 1366                 $parts explode(' '$key3);
  1367. 1367 
  1368. 1368                 $key = isset($parts[1]) ? base64_decode($parts[1]) : false;
  1369. 1369                 if ($key === false) {
  1370. 1370                     return false;
  1371. 1371                 }
  1372. 1372 
  1373. 1373                 $comment = isset($parts[2]) ? $parts[2] : false;
  1374. 1374 
  1375. 1375                 $cleanup substr($key011) == "\0\0\0\7ssh-rsa";
  1376. 1376 
  1377. 1377                 if (strlen($key) <= 4) {
  1378. 1378                     return false;
  1379. 1379                 }
  1380. 1380                 extract(unpack('Nlength'$this->_string_shift($key4)));
  1381. 1381                 $publicExponent = new Math_BigInteger($this->_string_shift($key$length), -256);
  1382. 1382                 if (strlen($key) <= 4) {
  1383. 1383                     return false;
  1384. 1384                 }
  1385. 1385                 extract(unpack('Nlength'$this->_string_shift($key4)));
  1386. 1386                 $modulus = new Math_BigInteger($this->_string_shift($key$length), -256);
  1387. 1387 
  1388. 1388                 if ($cleanup && strlen($key)) {
  1389. 1389                     if (strlen($key) <= 4) {
  1390. 1390                         return false;
  1391. 1391                     }
  1392. 1392                     extract(unpack('Nlength'$this->_string_shift($key4)));
  1393. 1393                     $realModulus = new Math_BigInteger($this->_string_shift($key$length), -256);
  1394. 1394                     return strlen($key) ? false : array(
  1395. 1395                         'modulus' => $realModulus,
  1396. 1396                         'publicExponent' => $modulus,
  1397. 1397                         'comment' => $comment
  1398. 1398                     );
  1399. 1399                 } else {
  1400. 1400                     return strlen($key) ? false : array(
  1401. 1401                         'modulus' => $modulus,
  1402. 1402                         'publicExponent' => $publicExponent,
  1403. 1403                         'comment' => $comment
  1404. 1404                     );
  1405. 1405                 }
  1406. 1406             // http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue
  1407. 1407             // http://en.wikipedia.org/wiki/XML_Signature
  1408. 1408             case CRYPT_RSA_PRIVATE_FORMAT_XML:
  1409. 1409             case CRYPT_RSA_PUBLIC_FORMAT_XML:
  1410. 1410                 $this->components = array();
  1411. 1411 
  1412. 1412                 $xml xml_parser_create('UTF-8');
  1413. 1413                 xml_set_object($xml$this);
  1414. 1414                 xml_set_element_handler($xml'_start_element_handler''_stop_element_handler');
  1415. 1415                 xml_set_character_data_handler($xml'_data_handler');
  1416. 1416                 // add <xml></xml> to account for "dangling" tags like <BitStrength>...</BitStrength> that are sometimes added
  1417. 1417                 if (!xml_parse($xml'<xml>' $key '</xml>')) {
  1418. 1418                     return false;
  1419. 1419                 }
  1420. 1420 
  1421. 1421                 return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components false;
  1422. 1422             // from PuTTY's SSHPUBK.C
  1423. 1423             case CRYPT_RSA_PRIVATE_FORMAT_PUTTY:
  1424. 1424                 $components = array();
  1425. 1425                 $key preg_split('#\r\n|\r|\n#'$key);
  1426. 1426                 $type trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#''$1'$key[0]));
  1427. 1427                 if ($type != 'ssh-rsa') {
  1428. 1428                     return false;
  1429. 1429                 }
  1430. 1430                 $encryption trim(preg_replace('#Encryption: (.+)#''$1'$key[1]));
  1431. 1431                 $comment trim(preg_replace('#Comment: (.+)#''$1'$key[2]));
  1432. 1432 
  1433. 1433                 $publicLength trim(preg_replace('#Public-Lines: (\d+)#''$1'$key[3]));
  1434. 1434                 $public base64_decode(implode(''array_map('trim'array_slice($key4$publicLength))));
  1435. 1435                 $public substr($public11);
  1436. 1436                 extract(unpack('Nlength'$this->_string_shift($public4)));
  1437. 1437                 $components['publicExponent'] = new Math_BigInteger($this->_string_shift($public$length), -256);
  1438. 1438                 extract(unpack('Nlength'$this->_string_shift($public4)));
  1439. 1439                 $components['modulus'] = new Math_BigInteger($this->_string_shift($public$length), -256);
  1440. 1440 
  1441. 1441                 $privateLength trim(preg_replace('#Private-Lines: (\d+)#''$1'$key[$publicLength 4]));
  1442. 1442                 $private base64_decode(implode(''array_map('trim'array_slice($key$publicLength 5$privateLength))));
  1443. 1443 
  1444. 1444                 switch ($encryption) {
  1445. 1445                     case 'aes256-cbc':
  1446. 1446                         if (!class_exists('Crypt_AES')) {
  1447. 1447                             include_once 'Crypt/AES.php';
  1448. 1448                         }
  1449. 1449                         $symkey '';
  1450. 1450                         $sequence 0;
  1451. 1451                         while (strlen($symkey) < 32) {
  1452. 1452                             $temp pack('Na*'$sequence++, $this->password);
  1453. 1453                             $symkey.= pack('H*'sha1($temp));
  1454. 1454                         }
  1455. 1455                         $symkey substr($symkey032);
  1456. 1456                         $crypto = new Crypt_AES();
  1457. 1457                 }
  1458. 1458 
  1459. 1459                 if ($encryption != 'none') {
  1460. 1460                     $crypto->setKey($symkey);
  1461. 1461                     $crypto->disablePadding();
  1462. 1462                     $private $crypto->decrypt($private);
  1463. 1463                     if ($private === false) {
  1464. 1464                         return false;
  1465. 1465                     }
  1466. 1466                 }
  1467. 1467 
  1468. 1468                 extract(unpack('Nlength'$this->_string_shift($private4)));
  1469. 1469                 if (strlen($private) < $length) {
  1470. 1470                     return false;
  1471. 1471                 }
  1472. 1472                 $components['privateExponent'] = new Math_BigInteger($this->_string_shift($private$length), -256);
  1473. 1473                 extract(unpack('Nlength'$this->_string_shift($private4)));
  1474. 1474                 if (strlen($private) < $length) {
  1475. 1475                     return false;
  1476. 1476                 }
  1477. 1477                 $components['primes'] = array(=> new Math_BigInteger($this->_string_shift($private$length), -256));
  1478. 1478                 extract(unpack('Nlength'$this->_string_shift($private4)));
  1479. 1479                 if (strlen($private) < $length) {
  1480. 1480                     return false;
  1481. 1481                 }
  1482. 1482                 $components['primes'][] = new Math_BigInteger($this->_string_shift($private$length), -256);
  1483. 1483 
  1484. 1484                 $temp $components['primes'][1]->subtract($this->one);
  1485. 1485                 $components['exponents'] = array(=> $components['publicExponent']->modInverse($temp));
  1486. 1486                 $temp $components['primes'][2]->subtract($this->one);
  1487. 1487                 $components['exponents'][] = $components['publicExponent']->modInverse($temp);
  1488. 1488 
  1489. 1489                 extract(unpack('Nlength'$this->_string_shift($private4)));
  1490. 1490                 if (strlen($private) < $length) {
  1491. 1491                     return false;
  1492. 1492                 }
  1493. 1493                 $components['coefficients'] = array(=> new Math_BigInteger($this->_string_shift($private$length), -256));
  1494. 1494 
  1495. 1495                 return $components;
  1496. 1496         }
  1497. 1497     }
  1498. 1498 
  1499. 1499     /**
  1500. 1500      * Returns the key size
  1501. 1501      *
  1502. 1502      * More specifically, this returns the size of the modulo in bits.
  1503. 1503      *
  1504. 1504      * @access public
  1505. 1505      * @return int
  1506. 1506      */
  1507. 1507     function getSize()
  1508. 1508     {
  1509. 1509         return !isset($this->modulus) ? strlen($this->modulus->toBits());
  1510. 1510     }
  1511. 1511 
  1512. 1512     /**
  1513. 1513      * Start Element Handler
  1514. 1514      *
  1515. 1515      * Called by xml_set_element_handler()
  1516. 1516      *
  1517. 1517      * @access private
  1518. 1518      * @param resource $parser
  1519. 1519      * @param string $name
  1520. 1520      * @param array $attribs
  1521. 1521      */
  1522. 1522     function _start_element_handler($parser$name$attribs)
  1523. 1523     {
  1524. 1524         //$name = strtoupper($name);
  1525. 1525         switch ($name) {
  1526. 1526             case 'MODULUS':
  1527. 1527                 $this->current = &$this->components['modulus'];
  1528. 1528                 break;
  1529. 1529             case 'EXPONENT':
  1530. 1530                 $this->current = &$this->components['publicExponent'];
  1531. 1531                 break;
  1532. 1532             case 'P':
  1533. 1533                 $this->current = &$this->components['primes'][1];
  1534. 1534                 break;
  1535. 1535             case 'Q':
  1536. 1536                 $this->current = &$this->components['primes'][2];
  1537. 1537                 break;
  1538. 1538             case 'DP':
  1539. 1539                 $this->current = &$this->components['exponents'][1];
  1540. 1540                 break;
  1541. 1541             case 'DQ':
  1542. 1542                 $this->current = &$this->components['exponents'][2];
  1543. 1543                 break;
  1544. 1544             case 'INVERSEQ':
  1545. 1545                 $this->current = &$this->components['coefficients'][2];
  1546. 1546                 break;
  1547. 1547             case 'D':
  1548. 1548                 $this->current = &$this->components['privateExponent'];
  1549. 1549         }
  1550. 1550         $this->current '';
  1551. 1551     }
  1552. 1552 
  1553. 1553     /**
  1554. 1554      * Stop Element Handler
  1555. 1555      *
  1556. 1556      * Called by xml_set_element_handler()
  1557. 1557      *
  1558. 1558      * @access private
  1559. 1559      * @param resource $parser
  1560. 1560      * @param string $name
  1561. 1561      */
  1562. 1562     function _stop_element_handler($parser$name)
  1563. 1563     {
  1564. 1564         if (isset($this->current)) {
  1565. 1565             $this->current = new Math_BigInteger(base64_decode($this->current), 256);
  1566. 1566             unset($this->current);
  1567. 1567         }
  1568. 1568     }
  1569. 1569 
  1570. 1570     /**
  1571. 1571      * Data Handler
  1572. 1572      *
  1573. 1573      * Called by xml_set_character_data_handler()
  1574. 1574      *
  1575. 1575      * @access private
  1576. 1576      * @param resource $parser
  1577. 1577      * @param string $data
  1578. 1578      */
  1579. 1579     function _data_handler($parser$data)
  1580. 1580     {
  1581. 1581         if (!isset($this->current) || is_object($this->current)) {
  1582. 1582             return;
  1583. 1583         }
  1584. 1584         $this->current.= trim($data);
  1585. 1585     }
  1586. 1586 
  1587. 1587     /**
  1588. 1588      * Loads a public or private key
  1589. 1589      *
  1590. 1590      * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed)
  1591. 1591      *
  1592. 1592      * @access public
  1593. 1593      * @param string $key
  1594. 1594      * @param int $type optional
  1595. 1595      */
  1596. 1596     function loadKey($key$type false)
  1597. 1597     {
  1598. 1598         if (is_object($key) && strtolower(get_class($key)) == 'crypt_rsa') {
  1599. 1599             $this->privateKeyFormat $key->privateKeyFormat;
  1600. 1600             $this->publicKeyFormat $key->publicKeyFormat;
  1601. 1601             $this->$key->k;
  1602. 1602             $this->hLen $key->hLen;
  1603. 1603             $this->sLen $key->sLen;
  1604. 1604             $this->mgfHLen $key->mgfHLen;
  1605. 1605             $this->encryptionMode $key->encryptionMode;
  1606. 1606             $this->signatureMode $key->signatureMode;
  1607. 1607             $this->password $key->password;
  1608. 1608             $this->configFile $key->configFile;
  1609. 1609             $this->comment $key->comment;
  1610. 1610 
  1611. 1611             if (is_object($key->hash)) {
  1612. 1612                 $this->hash = new Crypt_Hash($key->hash->getHash());
  1613. 1613             }
  1614. 1614             if (is_object($key->mgfHash)) {
  1615. 1615                 $this->mgfHash = new Crypt_Hash($key->mgfHash->getHash());
  1616. 1616             }
  1617. 1617 
  1618. 1618             if (is_object($key->modulus)) {
  1619. 1619                 $this->modulus $key->modulus->copy();
  1620. 1620             }
  1621. 1621             if (is_object($key->exponent)) {
  1622. 1622                 $this->exponent $key->exponent->copy();
  1623. 1623             }
  1624. 1624             if (is_object($key->publicExponent)) {
  1625. 1625                 $this->publicExponent $key->publicExponent->copy();
  1626. 1626             }
  1627. 1627 
  1628. 1628             $this->primes = array();
  1629. 1629             $this->exponents = array();
  1630. 1630             $this->coefficients = array();
  1631. 1631 
  1632. 1632             foreach ($this->primes as $prime) {
  1633. 1633                 $this->primes[] = $prime->copy();
  1634. 1634             }
  1635. 1635             foreach ($this->exponents as $exponent) {
  1636. 1636                 $this->exponents[] = $exponent->copy();
  1637. 1637             }
  1638. 1638             foreach ($this->coefficients as $coefficient) {
  1639. 1639                 $this->coefficients[] = $coefficient->copy();
  1640. 1640             }
  1641. 1641 
  1642. 1642             return true;
  1643. 1643         }
  1644. 1644 
  1645. 1645         if ($type === false) {
  1646. 1646             $types = array(
  1647. 1647                 CRYPT_RSA_PUBLIC_FORMAT_RAW,
  1648. 1648                 CRYPT_RSA_PRIVATE_FORMAT_PKCS1,
  1649. 1649                 CRYPT_RSA_PRIVATE_FORMAT_XML,
  1650. 1650                 CRYPT_RSA_PRIVATE_FORMAT_PUTTY,
  1651. 1651                 CRYPT_RSA_PUBLIC_FORMAT_OPENSSH
  1652. 1652             );
  1653. 1653             foreach ($types as $type) {
  1654. 1654                 $components $this->_parseKey($key$type);
  1655. 1655                 if ($components !== false) {
  1656. 1656                     break;
  1657. 1657                 }
  1658. 1658             }
  1659. 1659         } else {
  1660. 1660             $components $this->_parseKey($key$type);
  1661. 1661         }
  1662. 1662 
  1663. 1663         if ($components === false) {
  1664. 1664             $this->comment null;
  1665. 1665             $this->modulus null;
  1666. 1666             $this->null;
  1667. 1667             $this->exponent null;
  1668. 1668             $this->primes null;
  1669. 1669             $this->exponents null;
  1670. 1670             $this->coefficients null;
  1671. 1671             $this->publicExponent null;
  1672. 1672 
  1673. 1673             return false;
  1674. 1674         }
  1675. 1675 
  1676. 1676         if (isset($components['comment']) && $components['comment'] !== false) {
  1677. 1677             $this->comment $components['comment'];
  1678. 1678         }
  1679. 1679         $this->modulus $components['modulus'];
  1680. 1680         $this->strlen($this->modulus->toBytes());
  1681. 1681         $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
  1682. 1682         if (isset($components['primes'])) {
  1683. 1683             $this->primes $components['primes'];
  1684. 1684             $this->exponents $components['exponents'];
  1685. 1685             $this->coefficients $components['coefficients'];
  1686. 1686             $this->publicExponent $components['publicExponent'];
  1687. 1687         } else {
  1688. 1688             $this->primes = array();
  1689. 1689             $this->exponents = array();
  1690. 1690             $this->coefficients = array();
  1691. 1691             $this->publicExponent false;
  1692. 1692         }
  1693. 1693 
  1694. 1694         switch ($type) {
  1695. 1695             case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
  1696. 1696             case CRYPT_RSA_PUBLIC_FORMAT_RAW:
  1697. 1697                 $this->setPublicKey();
  1698. 1698                 break;
  1699. 1699             case CRYPT_RSA_PRIVATE_FORMAT_PKCS1:
  1700. 1700                 switch (true) {
  1701. 1701                     case strpos($key'-BEGIN PUBLIC KEY-') !== false:
  1702. 1702                     case strpos($key'-BEGIN RSA PUBLIC KEY-') !== false:
  1703. 1703                         $this->setPublicKey();
  1704. 1704                 }
  1705. 1705         }
  1706. 1706 
  1707. 1707         return true;
  1708. 1708     }
  1709. 1709 
  1710. 1710     /**
  1711. 1711      * Sets the password
  1712. 1712      *
  1713. 1713      * Private keys can be encrypted with a password.  To unset the password, pass in the empty string or false.
  1714. 1714      * Or rather, pass in $password such that empty($password) && !is_string($password) is true.
  1715. 1715      *
  1716. 1716      * @see self::createKey()
  1717. 1717      * @see self::loadKey()
  1718. 1718      * @access public
  1719. 1719      * @param string $password
  1720. 1720      */
  1721. 1721     function setPassword($password false)
  1722. 1722     {
  1723. 1723         $this->password $password;
  1724. 1724     }
  1725. 1725 
  1726. 1726     /**
  1727. 1727      * Defines the public key
  1728. 1728      *
  1729. 1729      * Some private key formats define the public exponent and some don't.  Those that don't define it are problematic when
  1730. 1730      * used in certain contexts.  For example, in SSH-2, RSA authentication works by sending the public key along with a
  1731. 1731      * message signed by the private key to the server.  The SSH-2 server looks the public key up in an index of public keys
  1732. 1732      * and if it's present then proceeds to verify the signature.  Problem is, if your private key doesn't include the public
  1733. 1733      * exponent this won't work unless you manually add the public exponent. phpseclib tries to guess if the key being used
  1734. 1734      * is the public key but in the event that it guesses incorrectly you might still want to explicitly set the key as being
  1735. 1735      * public.
  1736. 1736      *
  1737. 1737      * Do note that when a new key is loaded the index will be cleared.
  1738. 1738      *
  1739. 1739      * Returns true on success, false on failure
  1740. 1740      *
  1741. 1741      * @see self::getPublicKey()
  1742. 1742      * @access public
  1743. 1743      * @param string $key optional
  1744. 1744      * @param int $type optional
  1745. 1745      * @return bool
  1746. 1746      */
  1747. 1747     function setPublicKey($key false$type false)
  1748. 1748     {
  1749. 1749         // if a public key has already been loaded return false
  1750. 1750         if (!empty($this->publicExponent)) {
  1751. 1751             return false;
  1752. 1752         }
  1753. 1753 
  1754. 1754         if ($key === false && !empty($this->modulus)) {
  1755. 1755             $this->publicExponent $this->exponent;
  1756. 1756             return true;
  1757. 1757         }
  1758. 1758 
  1759. 1759         if ($type === false) {
  1760. 1760             $types = array(
  1761. 1761                 CRYPT_RSA_PUBLIC_FORMAT_RAW,
  1762. 1762                 CRYPT_RSA_PUBLIC_FORMAT_PKCS1,
  1763. 1763                 CRYPT_RSA_PUBLIC_FORMAT_XML,
  1764. 1764                 CRYPT_RSA_PUBLIC_FORMAT_OPENSSH
  1765. 1765             );
  1766. 1766             foreach ($types as $type) {
  1767. 1767                 $components $this->_parseKey($key$type);
  1768. 1768                 if ($components !== false) {
  1769. 1769                     break;
  1770. 1770                 }
  1771. 1771             }
  1772. 1772         } else {
  1773. 1773             $components $this->_parseKey($key$type);
  1774. 1774         }
  1775. 1775 
  1776. 1776         if ($components === false) {
  1777. 1777             return false;
  1778. 1778         }
  1779. 1779 
  1780. 1780         if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) {
  1781. 1781             $this->modulus $components['modulus'];
  1782. 1782             $this->exponent $this->publicExponent $components['publicExponent'];
  1783. 1783             return true;
  1784. 1784         }
  1785. 1785 
  1786. 1786         $this->publicExponent $components['publicExponent'];
  1787. 1787 
  1788. 1788         return true;
  1789. 1789     }
  1790. 1790 
  1791. 1791     /**
  1792. 1792      * Defines the private key
  1793. 1793      *
  1794. 1794      * If phpseclib guessed a private key was a public key and loaded it as such it might be desirable to force
  1795. 1795      * phpseclib to treat the key as a private key. This function will do that.
  1796. 1796      *
  1797. 1797      * Do note that when a new key is loaded the index will be cleared.
  1798. 1798      *
  1799. 1799      * Returns true on success, false on failure
  1800. 1800      *
  1801. 1801      * @see self::getPublicKey()
  1802. 1802      * @access public
  1803. 1803      * @param string $key optional
  1804. 1804      * @param int $type optional
  1805. 1805      * @return bool
  1806. 1806      */
  1807. 1807     function setPrivateKey($key false$type false)
  1808. 1808     {
  1809. 1809         if ($key === false && !empty($this->publicExponent)) {
  1810. 1810             $this->publicExponent false;
  1811. 1811             return true;
  1812. 1812         }
  1813. 1813 
  1814. 1814         $rsa = new Crypt_RSA();
  1815. 1815         if (!$rsa->loadKey($key$type)) {
  1816. 1816             return false;
  1817. 1817         }
  1818. 1818         $rsa->publicExponent false;
  1819. 1819 
  1820. 1820         // don't overwrite the old key if the new key is invalid
  1821. 1821         $this->loadKey($rsa);
  1822. 1822         return true;
  1823. 1823     }
  1824. 1824 
  1825. 1825     /**
  1826. 1826      * Returns the public key
  1827. 1827      *
  1828. 1828      * The public key is only returned under two circumstances - if the private key had the public key embedded within it
  1829. 1829      * or if the public key was set via setPublicKey().  If the currently loaded key is supposed to be the public key this
  1830. 1830      * function won't return it since this library, for the most part, doesn't distinguish between public and private keys.
  1831. 1831      *
  1832. 1832      * @see self::getPublicKey()
  1833. 1833      * @access public
  1834. 1834      * @param string $key
  1835. 1835      * @param int $type optional
  1836. 1836      */
  1837. 1837     function getPublicKey($type CRYPT_RSA_PUBLIC_FORMAT_PKCS8)
  1838. 1838     {
  1839. 1839         if (empty($this->modulus) || empty($this->publicExponent)) {
  1840. 1840             return false;
  1841. 1841         }
  1842. 1842 
  1843. 1843         $oldFormat $this->publicKeyFormat;
  1844. 1844         $this->publicKeyFormat $type;
  1845. 1845         $temp $this->_convertPublicKey($this->modulus$this->publicExponent);
  1846. 1846         $this->publicKeyFormat $oldFormat;
  1847. 1847         return $temp;
  1848. 1848     }
  1849. 1849 
  1850. 1850     /**
  1851. 1851      * Returns the public key's fingerprint
  1852. 1852      *
  1853. 1853      * The public key's fingerprint is returned, which is equivalent to running `ssh-keygen -lf rsa.pub`. If there is
  1854. 1854      * no public key currently loaded, false is returned.
  1855. 1855      * Example output (md5): "c1:b1:30:29:d7:b8:de:6c:97:77:10:d7:46:41:63:87" (as specified by RFC 4716)
  1856. 1856      *
  1857. 1857      * @access public
  1858. 1858      * @param string $algorithm The hashing algorithm to be used. Valid options are 'md5' and 'sha256'. False is returned
  1859. 1859      * for invalid values.
  1860. 1860      * @return mixed
  1861. 1861      */
  1862. 1862     function getPublicKeyFingerprint($algorithm 'md5')
  1863. 1863     {
  1864. 1864         if (empty($this->modulus) || empty($this->publicExponent)) {
  1865. 1865             return false;
  1866. 1866         }
  1867. 1867 
  1868. 1868         $modulus $this->modulus->toBytes(true);
  1869. 1869         $publicExponent $this->publicExponent->toBytes(true);
  1870. 1870 
  1871. 1871         $RSAPublicKey pack('Na*Na*Na*'strlen('ssh-rsa'), 'ssh-rsa'strlen($publicExponent), $publicExponentstrlen($modulus), $modulus);
  1872. 1872 
  1873. 1873         switch ($algorithm) {
  1874. 1874             case 'sha256':
  1875. 1875                 $hash = new Crypt_Hash('sha256');
  1876. 1876                 $base base64_encode($hash->hash($RSAPublicKey));
  1877. 1877                 return substr($base0strlen($base) - 1);
  1878. 1878             case 'md5':
  1879. 1879                 return substr(chunk_split(md5($RSAPublicKey), 2':'), 0, -1);
  1880. 1880             default:
  1881. 1881                 return false;
  1882. 1882         }
  1883. 1883     }
  1884. 1884 
  1885. 1885     /**
  1886. 1886      * Returns the private key
  1887. 1887      *
  1888. 1888      * The private key is only returned if the currently loaded key contains the constituent prime numbers.
  1889. 1889      *
  1890. 1890      * @see self::getPublicKey()
  1891. 1891      * @access public
  1892. 1892      * @param string $key
  1893. 1893      * @param int $type optional
  1894. 1894      * @return mixed
  1895. 1895      */
  1896. 1896     function getPrivateKey($type CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
  1897. 1897     {
  1898. 1898         if (empty($this->primes)) {
  1899. 1899             return false;
  1900. 1900         }
  1901. 1901 
  1902. 1902         $oldFormat $this->privateKeyFormat;
  1903. 1903         $this->privateKeyFormat $type;
  1904. 1904         $temp $this->_convertPrivateKey($this->modulus$this->publicExponent$this->exponent$this->primes$this->exponents$this->coefficients);
  1905. 1905         $this->privateKeyFormat $oldFormat;
  1906. 1906         return $temp;
  1907. 1907     }
  1908. 1908 
  1909. 1909     /**
  1910. 1910      * Returns a minimalistic private key
  1911. 1911      *
  1912. 1912      * Returns the private key without the prime number constituants.  Structurally identical to a public key that
  1913. 1913      * hasn't been set as the public key
  1914. 1914      *
  1915. 1915      * @see self::getPrivateKey()
  1916. 1916      * @access private
  1917. 1917      * @param string $key
  1918. 1918      * @param int $type optional
  1919. 1919      */
  1920. 1920     function _getPrivatePublicKey($mode CRYPT_RSA_PUBLIC_FORMAT_PKCS8)
  1921. 1921     {
  1922. 1922         if (empty($this->modulus) || empty($this->exponent)) {
  1923. 1923             return false;
  1924. 1924         }
  1925. 1925 
  1926. 1926         $oldFormat $this->publicKeyFormat;
  1927. 1927         $this->publicKeyFormat $mode;
  1928. 1928         $temp $this->_convertPublicKey($this->modulus$this->exponent);
  1929. 1929         $this->publicKeyFormat $oldFormat;
  1930. 1930         return $temp;
  1931. 1931     }
  1932. 1932 
  1933. 1933     /**
  1934. 1934      *  __toString() magic method
  1935. 1935      *
  1936. 1936      * @access public
  1937. 1937      * @return string
  1938. 1938      */
  1939. 1939     function __toString()
  1940. 1940     {
  1941. 1941         $key $this->getPrivateKey($this->privateKeyFormat);
  1942. 1942         if ($key !== false) {
  1943. 1943             return $key;
  1944. 1944         }
  1945. 1945         $key $this->_getPrivatePublicKey($this->publicKeyFormat);
  1946. 1946         return $key !== false $key '';
  1947. 1947     }
  1948. 1948 
  1949. 1949     /**
  1950. 1950      *  __clone() magic method
  1951. 1951      *
  1952. 1952      * @access public
  1953. 1953      * @return Crypt_RSA
  1954. 1954      */
  1955. 1955     function __clone()
  1956. 1956     {
  1957. 1957         $key = new Crypt_RSA();
  1958. 1958         $key->loadKey($this);
  1959. 1959         return $key;
  1960. 1960     }
  1961. 1961 
  1962. 1962     /**
  1963. 1963      * Generates the smallest and largest numbers requiring $bits bits
  1964. 1964      *
  1965. 1965      * @access private
  1966. 1966      * @param int $bits
  1967. 1967      * @return array
  1968. 1968      */
  1969. 1969     function _generateMinMax($bits)
  1970. 1970     {
  1971. 1971         $bytes $bits >> 3;
  1972. 1972         $min str_repeat(chr(0), $bytes);
  1973. 1973         $max str_repeat(chr(0xFF), $bytes);
  1974. 1974         $msb $bits 7;
  1975. 1975         if ($msb) {
  1976. 1976             $min chr(<< ($msb 1)) . $min;
  1977. 1977             $max chr((<< $msb) - 1) . $max;
  1978. 1978         } else {
  1979. 1979             $min[0] = chr(0x80);
  1980. 1980         }
  1981. 1981 
  1982. 1982         return array(
  1983. 1983             'min' => new Math_BigInteger($min256),
  1984. 1984             'max' => new Math_BigInteger($max256)
  1985. 1985         );
  1986. 1986     }
  1987. 1987 
  1988. 1988     /**
  1989. 1989      * DER-decode the length
  1990. 1990      *
  1991. 1991      * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4.  See
  1992. 1992      * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
  1993. 1993      *
  1994. 1994      * @access private
  1995. 1995      * @param string $string
  1996. 1996      * @return int
  1997. 1997      */
  1998. 1998     function _decodeLength(&$string)
  1999. 1999     {
  2000. 2000         $length ord($this->_string_shift($string));
  2001. 2001         if ($length 0x80) { // definite length, long form
  2002. 2002             $length&= 0x7F;
  2003. 2003             $temp $this->_string_shift($string$length);
  2004. 2004             list(, $length) = unpack('N'substr(str_pad($temp4chr(0), STR_PAD_LEFT), -4));
  2005. 2005         }
  2006. 2006         return $length;
  2007. 2007     }
  2008. 2008 
  2009. 2009     /**
  2010. 2010      * DER-encode the length
  2011. 2011      *
  2012. 2012      * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4.  See
  2013. 2013      * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
  2014. 2014      *
  2015. 2015      * @access private
  2016. 2016      * @param int $length
  2017. 2017      * @return string
  2018. 2018      */
  2019. 2019     function _encodeLength($length)
  2020. 2020     {
  2021. 2021         if ($length <= 0x7F) {
  2022. 2022             return chr($length);
  2023. 2023         }
  2024. 2024 
  2025. 2025         $temp ltrim(pack('N'$length), chr(0));
  2026. 2026         return pack('Ca*'0x80 strlen($temp), $temp);
  2027. 2027     }
  2028. 2028 
  2029. 2029     /**
  2030. 2030      * String Shift
  2031. 2031      *
  2032. 2032      * Inspired by array_shift
  2033. 2033      *
  2034. 2034      * @param string $string
  2035. 2035      * @param int $index
  2036. 2036      * @return string
  2037. 2037      * @access private
  2038. 2038      */
  2039. 2039     function _string_shift(&$string$index 1)
  2040. 2040     {
  2041. 2041         $substr substr($string0$index);
  2042. 2042         $string substr($string$index);
  2043. 2043         return $substr;
  2044. 2044     }
  2045. 2045 
  2046. 2046     /**
  2047. 2047      * Determines the private key format
  2048. 2048      *
  2049. 2049      * @see self::createKey()
  2050. 2050      * @access public
  2051. 2051      * @param int $format
  2052. 2052      */
  2053. 2053     function setPrivateKeyFormat($format)
  2054. 2054     {
  2055. 2055         $this->privateKeyFormat $format;
  2056. 2056     }
  2057. 2057 
  2058. 2058     /**
  2059. 2059      * Determines the public key format
  2060. 2060      *
  2061. 2061      * @see self::createKey()
  2062. 2062      * @access public
  2063. 2063      * @param int $format
  2064. 2064      */
  2065. 2065     function setPublicKeyFormat($format)
  2066. 2066     {
  2067. 2067         $this->publicKeyFormat $format;
  2068. 2068     }
  2069. 2069 
  2070. 2070     /**
  2071. 2071      * Determines which hashing function should be used
  2072. 2072      *
  2073. 2073      * Used with signature production / verification and (if the encryption mode is CRYPT_RSA_ENCRYPTION_OAEP) encryption and
  2074. 2074      * decryption.  If $hash isn't supported, sha1 is used.
  2075. 2075      *
  2076. 2076      * @access public
  2077. 2077      * @param string $hash
  2078. 2078      */
  2079. 2079     function setHash($hash)
  2080. 2080     {
  2081. 2081         // Crypt_Hash supports algorithms that PKCS#1 doesn't support.  md5-96 and sha1-96, for example.
  2082. 2082         switch ($hash) {
  2083. 2083             case 'md2':
  2084. 2084             case 'md5':
  2085. 2085             case 'sha1':
  2086. 2086             case 'sha256':
  2087. 2087             case 'sha384':
  2088. 2088             case 'sha512':
  2089. 2089                 $this->hash = new Crypt_Hash($hash);
  2090. 2090                 $this->hashName $hash;
  2091. 2091                 break;
  2092. 2092             default:
  2093. 2093                 $this->hash = new Crypt_Hash('sha1');
  2094. 2094                 $this->hashName 'sha1';
  2095. 2095         }
  2096. 2096         $this->hLen $this->hash->getLength();
  2097. 2097     }
  2098. 2098 
  2099. 2099     /**
  2100. 2100      * Determines which hashing function should be used for the mask generation function
  2101. 2101      *
  2102. 2102      * The mask generation function is used by CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_SIGNATURE_PSS and although it's
  2103. 2103      * best if Hash and MGFHash are set to the same thing this is not a requirement.
  2104. 2104      *
  2105. 2105      * @access public
  2106. 2106      * @param string $hash
  2107. 2107      */
  2108. 2108     function setMGFHash($hash)
  2109. 2109     {
  2110. 2110         // Crypt_Hash supports algorithms that PKCS#1 doesn't support.  md5-96 and sha1-96, for example.
  2111. 2111         switch ($hash) {
  2112. 2112             case 'md2':
  2113. 2113             case 'md5':
  2114. 2114             case 'sha1':
  2115. 2115             case 'sha256':
  2116. 2116             case 'sha384':
  2117. 2117             case 'sha512':
  2118. 2118                 $this->mgfHash = new Crypt_Hash($hash);
  2119. 2119                 break;
  2120. 2120             default:
  2121. 2121                 $this->mgfHash = new Crypt_Hash('sha1');
  2122. 2122         }
  2123. 2123         $this->mgfHLen $this->mgfHash->getLength();
  2124. 2124     }
  2125. 2125 
  2126. 2126     /**
  2127. 2127      * Determines the salt length
  2128. 2128      *
  2129. 2129      * To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}:
  2130. 2130      *
  2131. 2131      *    Typical salt lengths in octets are hLen (the length of the output
  2132. 2132      *    of the hash function Hash) and 0.
  2133. 2133      *
  2134. 2134      * @access public
  2135. 2135      * @param int $format
  2136. 2136      */
  2137. 2137     function setSaltLength($sLen)
  2138. 2138     {
  2139. 2139         $this->sLen $sLen;
  2140. 2140     }
  2141. 2141 
  2142. 2142     /**
  2143. 2143      * Integer-to-Octet-String primitive
  2144. 2144      *
  2145. 2145      * See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}.
  2146. 2146      *
  2147. 2147      * @access private
  2148. 2148      * @param Math_BigInteger $x
  2149. 2149      * @param int $xLen
  2150. 2150      * @return string
  2151. 2151      */
  2152. 2152     function _i2osp($x$xLen)
  2153. 2153     {
  2154. 2154         $x $x->toBytes();
  2155. 2155         if (strlen($x) > $xLen) {
  2156. 2156             user_error('Integer too large');
  2157. 2157             return false;
  2158. 2158         }
  2159. 2159         return str_pad($x$xLenchr(0), STR_PAD_LEFT);
  2160. 2160     }
  2161. 2161 
  2162. 2162     /**
  2163. 2163      * Octet-String-to-Integer primitive
  2164. 2164      *
  2165. 2165      * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}.
  2166. 2166      *
  2167. 2167      * @access private
  2168. 2168      * @param string $x
  2169. 2169      * @return Math_BigInteger
  2170. 2170      */
  2171. 2171     function _os2ip($x)
  2172. 2172     {
  2173. 2173         return new Math_BigInteger($x256);
  2174. 2174     }
  2175. 2175 
  2176. 2176     /**
  2177. 2177      * Exponentiate with or without Chinese Remainder Theorem
  2178. 2178      *
  2179. 2179      * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.2}.
  2180. 2180      *
  2181. 2181      * @access private
  2182. 2182      * @param Math_BigInteger $x
  2183. 2183      * @return Math_BigInteger
  2184. 2184      */
  2185. 2185     function _exponentiate($x)
  2186. 2186     {
  2187. 2187         switch (true) {
  2188. 2188             case empty($this->primes):
  2189. 2189             case $this->primes[1]->equals($this->zero):
  2190. 2190             case empty($this->coefficients):
  2191. 2191             case $this->coefficients[2]->equals($this->zero):
  2192. 2192             case empty($this->exponents):
  2193. 2193             case $this->exponents[1]->equals($this->zero):
  2194. 2194                 return $x->modPow($this->exponent$this->modulus);
  2195. 2195         }
  2196. 2196 
  2197. 2197         $num_primes count($this->primes);
  2198. 2198 
  2199. 2199         if (defined('CRYPT_RSA_DISABLE_BLINDING')) {
  2200. 2200             $m_i = array(
  2201. 2201                 => $x->modPow($this->exponents[1], $this->primes[1]),
  2202. 2202                 => $x->modPow($this->exponents[2], $this->primes[2])
  2203. 2203             );
  2204. 2204             $h $m_i[1]->subtract($m_i[2]);
  2205. 2205             $h $h->multiply($this->coefficients[2]);
  2206. 2206             list(, $h) = $h->divide($this->primes[1]);
  2207. 2207             $m $m_i[2]->add($h->multiply($this->primes[2]));
  2208. 2208 
  2209. 2209             $r $this->primes[1];
  2210. 2210             for ($i 3$i <= $num_primes$i++) {
  2211. 2211                 $m_i $x->modPow($this->exponents[$i], $this->primes[$i]);
  2212. 2212 
  2213. 2213                 $r $r->multiply($this->primes[$i 1]);
  2214. 2214 
  2215. 2215                 $h $m_i->subtract($m);
  2216. 2216                 $h $h->multiply($this->coefficients[$i]);
  2217. 2217                 list(, $h) = $h->divide($this->primes[$i]);
  2218. 2218 
  2219. 2219                 $m $m->add($r->multiply($h));
  2220. 2220             }
  2221. 2221         } else {
  2222. 2222             $smallest $this->primes[1];
  2223. 2223             for ($i 2$i <= $num_primes$i++) {
  2224. 2224                 if ($smallest->compare($this->primes[$i]) > 0) {
  2225. 2225                     $smallest $this->primes[$i];
  2226. 2226                 }
  2227. 2227             }
  2228. 2228 
  2229. 2229             $one = new Math_BigInteger(1);
  2230. 2230 
  2231. 2231             $r $one->random($one$smallest->subtract($one));
  2232. 2232 
  2233. 2233             $m_i = array(
  2234. 2234                 => $this->_blind($x$r1),
  2235. 2235                 => $this->_blind($x$r2)
  2236. 2236             );
  2237. 2237             $h $m_i[1]->subtract($m_i[2]);
  2238. 2238             $h $h->multiply($this->coefficients[2]);
  2239. 2239             list(, $h) = $h->divide($this->primes[1]);
  2240. 2240             $m $m_i[2]->add($h->multiply($this->primes[2]));
  2241. 2241 
  2242. 2242             $r $this->primes[1];
  2243. 2243             for ($i 3$i <= $num_primes$i++) {
  2244. 2244                 $m_i $this->_blind($x$r$i);
  2245. 2245 
  2246. 2246                 $r $r->multiply($this->primes[$i 1]);
  2247. 2247 
  2248. 2248                 $h $m_i->subtract($m);
  2249. 2249                 $h $h->multiply($this->coefficients[$i]);
  2250. 2250                 list(, $h) = $h->divide($this->primes[$i]);
  2251. 2251 
  2252. 2252                 $m $m->add($r->multiply($h));
  2253. 2253             }
  2254. 2254         }
  2255. 2255 
  2256. 2256         return $m;
  2257. 2257     }
  2258. 2258 
  2259. 2259     /**
  2260. 2260      * Performs RSA Blinding
  2261. 2261      *
  2262. 2262      * Protects against timing attacks by employing RSA Blinding.
  2263. 2263      * Returns $x->modPow($this->exponents[$i], $this->primes[$i])
  2264. 2264      *
  2265. 2265      * @access private
  2266. 2266      * @param Math_BigInteger $x
  2267. 2267      * @param Math_BigInteger $r
  2268. 2268      * @param int $i
  2269. 2269      * @return Math_BigInteger
  2270. 2270      */
  2271. 2271     function _blind($x$r$i)
  2272. 2272     {
  2273. 2273         $x $x->multiply($r->modPow($this->publicExponent$this->primes[$i]));
  2274. 2274         $x $x->modPow($this->exponents[$i], $this->primes[$i]);
  2275. 2275 
  2276. 2276         $r $r->modInverse($this->primes[$i]);
  2277. 2277         $x $x->multiply($r);
  2278. 2278         list(, $x) = $x->divide($this->primes[$i]);
  2279. 2279 
  2280. 2280         return $x;
  2281. 2281     }
  2282. 2282 
  2283. 2283     /**
  2284. 2284      * Performs blinded RSA equality testing
  2285. 2285      *
  2286. 2286      * Protects against a particular type of timing attack described.
  2287. 2287      *
  2288. 2288      * See {@link http://codahale.com/a-lesson-in-timing-attacks/ A Lesson In Timing Attacks (or, Don't use MessageDigest.isEquals)}
  2289. 2289      *
  2290. 2290      * Thanks for the heads up singpolyma!
  2291. 2291      *
  2292. 2292      * @access private
  2293. 2293      * @param string $x
  2294. 2294      * @param string $y
  2295. 2295      * @return bool
  2296. 2296      */
  2297. 2297     function _equals($x$y)
  2298. 2298     {
  2299. 2299         if (strlen($x) != strlen($y)) {
  2300. 2300             return false;
  2301. 2301         }
  2302. 2302 
  2303. 2303         $result 0;
  2304. 2304         for ($i 0$i strlen($x); $i++) {
  2305. 2305             $result |= ord($x[$i]) ^ ord($y[$i]);
  2306. 2306         }
  2307. 2307 
  2308. 2308         return $result == 0;
  2309. 2309     }
  2310. 2310 
  2311. 2311     /**
  2312. 2312      * RSAEP
  2313. 2313      *
  2314. 2314      * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}.
  2315. 2315      *
  2316. 2316      * @access private
  2317. 2317      * @param Math_BigInteger $m
  2318. 2318      * @return Math_BigInteger
  2319. 2319      */
  2320. 2320     function _rsaep($m)
  2321. 2321     {
  2322. 2322         if ($m->compare($this->zero) < || $m->compare($this->modulus) > 0) {
  2323. 2323             user_error('Message representative out of range');
  2324. 2324             return false;
  2325. 2325         }
  2326. 2326         return $this->_exponentiate($m);
  2327. 2327     }
  2328. 2328 
  2329. 2329     /**
  2330. 2330      * RSADP
  2331. 2331      *
  2332. 2332      * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}.
  2333. 2333      *
  2334. 2334      * @access private
  2335. 2335      * @param Math_BigInteger $c
  2336. 2336      * @return Math_BigInteger
  2337. 2337      */
  2338. 2338     function _rsadp($c)
  2339. 2339     {
  2340. 2340         if ($c->compare($this->zero) < || $c->compare($this->modulus) > 0) {
  2341. 2341             user_error('Ciphertext representative out of range');
  2342. 2342             return false;
  2343. 2343         }
  2344. 2344         return $this->_exponentiate($c);
  2345. 2345     }
  2346. 2346 
  2347. 2347     /**
  2348. 2348      * RSASP1
  2349. 2349      *
  2350. 2350      * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}.
  2351. 2351      *
  2352. 2352      * @access private
  2353. 2353      * @param Math_BigInteger $m
  2354. 2354      * @return Math_BigInteger
  2355. 2355      */
  2356. 2356     function _rsasp1($m)
  2357. 2357     {
  2358. 2358         if ($m->compare($this->zero) < || $m->compare($this->modulus) > 0) {
  2359. 2359             user_error('Message representative out of range');
  2360. 2360             return false;
  2361. 2361         }
  2362. 2362         return $this->_exponentiate($m);
  2363. 2363     }
  2364. 2364 
  2365. 2365     /**
  2366. 2366      * RSAVP1
  2367. 2367      *
  2368. 2368      * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}.
  2369. 2369      *
  2370. 2370      * @access private
  2371. 2371      * @param Math_BigInteger $s
  2372. 2372      * @return Math_BigInteger
  2373. 2373      */
  2374. 2374     function _rsavp1($s)
  2375. 2375     {
  2376. 2376         if ($s->compare($this->zero) < || $s->compare($this->modulus) > 0) {
  2377. 2377             user_error('Signature representative out of range');
  2378. 2378             return false;
  2379. 2379         }
  2380. 2380         return $this->_exponentiate($s);
  2381. 2381     }
  2382. 2382 
  2383. 2383     /**
  2384. 2384      * MGF1
  2385. 2385      *
  2386. 2386      * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}.
  2387. 2387      *
  2388. 2388      * @access private
  2389. 2389      * @param string $mgfSeed
  2390. 2390      * @param int $mgfLen
  2391. 2391      * @return string
  2392. 2392      */
  2393. 2393     function _mgf1($mgfSeed$maskLen)
  2394. 2394     {
  2395. 2395         // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.
  2396. 2396 
  2397. 2397         $t '';
  2398. 2398         $count ceil($maskLen $this->mgfHLen);
  2399. 2399         for ($i 0$i $count$i++) {
  2400. 2400             $c pack('N'$i);
  2401. 2401             $t.= $this->mgfHash->hash($mgfSeed $c);
  2402. 2402         }
  2403. 2403 
  2404. 2404         return substr($t0$maskLen);
  2405. 2405     }
  2406. 2406 
  2407. 2407     /**
  2408. 2408      * RSAES-OAEP-ENCRYPT
  2409. 2409      *
  2410. 2410      * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and
  2411. 2411      * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}.
  2412. 2412      *
  2413. 2413      * @access private
  2414. 2414      * @param string $m
  2415. 2415      * @param string $l
  2416. 2416      * @return string
  2417. 2417      */
  2418. 2418     function _rsaes_oaep_encrypt($m$l '')
  2419. 2419     {
  2420. 2420         $mLen strlen($m);
  2421. 2421 
  2422. 2422         // Length checking
  2423. 2423 
  2424. 2424         // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
  2425. 2425         // be output.
  2426. 2426 
  2427. 2427         if ($mLen $this->$this->hLen 2) {
  2428. 2428             user_error('Message too long');
  2429. 2429             return false;
  2430. 2430         }
  2431. 2431 
  2432. 2432         // EME-OAEP encoding
  2433. 2433 
  2434. 2434         $lHash $this->hash->hash($l);
  2435. 2435         $ps str_repeat(chr(0), $this->$mLen $this->hLen 2);
  2436. 2436         $db $lHash $ps chr(1) . $m;
  2437. 2437         $seed crypt_random_string($this->hLen);
  2438. 2438         $dbMask $this->_mgf1($seed$this->$this->hLen 1);
  2439. 2439         $maskedDB $db $dbMask;
  2440. 2440         $seedMask $this->_mgf1($maskedDB$this->hLen);
  2441. 2441         $maskedSeed $seed $seedMask;
  2442. 2442         $em chr(0) . $maskedSeed $maskedDB;
  2443. 2443 
  2444. 2444         // RSA encryption
  2445. 2445 
  2446. 2446         $m $this->_os2ip($em);
  2447. 2447         $c $this->_rsaep($m);
  2448. 2448         $c $this->_i2osp($c$this->k);
  2449. 2449 
  2450. 2450         // Output the ciphertext C
  2451. 2451 
  2452. 2452         return $c;
  2453. 2453     }
  2454. 2454 
  2455. 2455     /**
  2456. 2456      * RSAES-OAEP-DECRYPT
  2457. 2457      *
  2458. 2458      * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}.  The fact that the error
  2459. 2459      * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2:
  2460. 2460      *
  2461. 2461      *    Note.  Care must be taken to ensure that an opponent cannot
  2462. 2462      *    distinguish the different error conditions in Step 3.g, whether by
  2463. 2463      *    error message or timing, or, more generally, learn partial
  2464. 2464      *    information about the encoded message EM.  Otherwise an opponent may
  2465. 2465      *    be able to obtain useful information about the decryption of the
  2466. 2466      *    ciphertext C, leading to a chosen-ciphertext attack such as the one
  2467. 2467      *    observed by Manger [36].
  2468. 2468      *
  2469. 2469      * As for $l...  to quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}:
  2470. 2470      *
  2471. 2471      *    Both the encryption and the decryption operations of RSAES-OAEP take
  2472. 2472      *    the value of a label L as input.  In this version of PKCS #1, L is
  2473. 2473      *    the empty string; other uses of the label are outside the scope of
  2474. 2474      *    this document.
  2475. 2475      *
  2476. 2476      * @access private
  2477. 2477      * @param string $c
  2478. 2478      * @param string $l
  2479. 2479      * @return string
  2480. 2480      */
  2481. 2481     function _rsaes_oaep_decrypt($c$l '')
  2482. 2482     {
  2483. 2483         // Length checking
  2484. 2484 
  2485. 2485         // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
  2486. 2486         // be output.
  2487. 2487 
  2488. 2488         if (strlen($c) != $this->|| $this->$this->hLen 2) {
  2489. 2489             user_error('Decryption error');
  2490. 2490             return false;
  2491. 2491         }
  2492. 2492 
  2493. 2493         // RSA decryption
  2494. 2494 
  2495. 2495         $c $this->_os2ip($c);
  2496. 2496         $m $this->_rsadp($c);
  2497. 2497         if ($m === false) {
  2498. 2498             user_error('Decryption error');
  2499. 2499             return false;
  2500. 2500         }
  2501. 2501         $em $this->_i2osp($m$this->k);
  2502. 2502 
  2503. 2503         // EME-OAEP decoding
  2504. 2504 
  2505. 2505         $lHash $this->hash->hash($l);
  2506. 2506         $y ord($em[0]);
  2507. 2507         $maskedSeed substr($em1$this->hLen);
  2508. 2508         $maskedDB substr($em$this->hLen 1);
  2509. 2509         $seedMask $this->_mgf1($maskedDB$this->hLen);
  2510. 2510         $seed $maskedSeed $seedMask;
  2511. 2511         $dbMask $this->_mgf1($seed$this->$this->hLen 1);
  2512. 2512         $db $maskedDB $dbMask;
  2513. 2513         $lHash2 substr($db0$this->hLen);
  2514. 2514         $m substr($db$this->hLen);
  2515. 2515         if (!$this->_equals($lHash$lHash2)) {
  2516. 2516             user_error('Decryption error');
  2517. 2517             return false;
  2518. 2518         }
  2519. 2519         $m ltrim($mchr(0));
  2520. 2520         if (ord($m[0]) != 1) {
  2521. 2521             user_error('Decryption error');
  2522. 2522             return false;
  2523. 2523         }
  2524. 2524 
  2525. 2525         // Output the message M
  2526. 2526 
  2527. 2527         return substr($m1);
  2528. 2528     }
  2529. 2529 
  2530. 2530     /**
  2531. 2531      * Raw Encryption / Decryption
  2532. 2532      *
  2533. 2533      * Doesn't use padding and is not recommended.
  2534. 2534      *
  2535. 2535      * @access private
  2536. 2536      * @param string $m
  2537. 2537      * @return string
  2538. 2538      */
  2539. 2539     function _raw_encrypt($m)
  2540. 2540     {
  2541. 2541         $temp $this->_os2ip($m);
  2542. 2542         $temp $this->_rsaep($temp);
  2543. 2543         return  $this->_i2osp($temp$this->k);
  2544. 2544     }
  2545. 2545 
  2546. 2546     /**
  2547. 2547      * RSAES-PKCS1-V1_5-ENCRYPT
  2548. 2548      *
  2549. 2549      * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}.
  2550. 2550      *
  2551. 2551      * @access private
  2552. 2552      * @param string $m
  2553. 2553      * @return string
  2554. 2554      */
  2555. 2555     function _rsaes_pkcs1_v1_5_encrypt($m)
  2556. 2556     {
  2557. 2557         $mLen strlen($m);
  2558. 2558 
  2559. 2559         // Length checking
  2560. 2560 
  2561. 2561         if ($mLen $this->11) {
  2562. 2562             user_error('Message too long');
  2563. 2563             return false;
  2564. 2564         }
  2565. 2565 
  2566. 2566         // EME-PKCS1-v1_5 encoding
  2567. 2567 
  2568. 2568         $psLen $this->$mLen 3;
  2569. 2569         $ps '';
  2570. 2570         while (strlen($ps) != $psLen) {
  2571. 2571             $temp crypt_random_string($psLen strlen($ps));
  2572. 2572             $temp str_replace("\x00"''$temp);
  2573. 2573             $ps.= $temp;
  2574. 2574         }
  2575. 2575         $type 2;
  2576. 2576         // see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand why this is being done
  2577. 2577         if (defined('CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) {
  2578. 2578             $type 1;
  2579. 2579             // "The padding string PS shall consist of k-3-||D|| octets. ... for block type 01, they shall have value FF"
  2580. 2580             $ps str_repeat("\xFF"$psLen);
  2581. 2581         }
  2582. 2582         $em chr(0) . chr($type) . $ps chr(0) . $m;
  2583. 2583 
  2584. 2584         // RSA encryption
  2585. 2585         $m $this->_os2ip($em);
  2586. 2586         $c $this->_rsaep($m);
  2587. 2587         $c $this->_i2osp($c$this->k);
  2588. 2588 
  2589. 2589         // Output the ciphertext C
  2590. 2590 
  2591. 2591         return $c;
  2592. 2592     }
  2593. 2593 
  2594. 2594     /**
  2595. 2595      * RSAES-PKCS1-V1_5-DECRYPT
  2596. 2596      *
  2597. 2597      * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}.
  2598. 2598      *
  2599. 2599      * For compatibility purposes, this function departs slightly from the description given in RFC3447.
  2600. 2600      * The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the
  2601. 2601      * private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the
  2602. 2602      * public key should have the second byte set to 2.  In RFC3447 (PKCS#1 v2.1), the second byte is supposed
  2603. 2603      * to be 2 regardless of which key is used.  For compatibility purposes, we'll just check to make sure the
  2604. 2604      * second byte is 2 or less.  If it is, we'll accept the decrypted string as valid.
  2605. 2605      *
  2606. 2606      * As a consequence of this, a private key encrypted ciphertext produced with Crypt_RSA may not decrypt
  2607. 2607      * with a strictly PKCS#1 v1.5 compliant RSA implementation.  Public key encrypted ciphertext's should but
  2608. 2608      * not private key encrypted ciphertext's.
  2609. 2609      *
  2610. 2610      * @access private
  2611. 2611      * @param string $c
  2612. 2612      * @return string
  2613. 2613      */
  2614. 2614     function _rsaes_pkcs1_v1_5_decrypt($c)
  2615. 2615     {
  2616. 2616         // Length checking
  2617. 2617 
  2618. 2618         if (strlen($c) != $this->k) { // or if k < 11
  2619. 2619             user_error('Decryption error');
  2620. 2620             return false;
  2621. 2621         }
  2622. 2622 
  2623. 2623         // RSA decryption
  2624. 2624 
  2625. 2625         $c $this->_os2ip($c);
  2626. 2626         $m $this->_rsadp($c);
  2627. 2627 
  2628. 2628         if ($m === false) {
  2629. 2629             user_error('Decryption error');
  2630. 2630             return false;
  2631. 2631         }
  2632. 2632         $em $this->_i2osp($m$this->k);
  2633. 2633 
  2634. 2634         // EME-PKCS1-v1_5 decoding
  2635. 2635 
  2636. 2636         if (ord($em[0]) != || ord($em[1]) > 2) {
  2637. 2637             user_error('Decryption error');
  2638. 2638             return false;
  2639. 2639         }
  2640. 2640 
  2641. 2641         $ps substr($em2strpos($emchr(0), 2) - 2);
  2642. 2642         $m substr($emstrlen($ps) + 3);
  2643. 2643 
  2644. 2644         if (strlen($ps) < 8) {
  2645. 2645             user_error('Decryption error');
  2646. 2646             return false;
  2647. 2647         }
  2648. 2648 
  2649. 2649         // Output M
  2650. 2650 
  2651. 2651         return $m;
  2652. 2652     }
  2653. 2653 
  2654. 2654     /**
  2655. 2655      * EMSA-PSS-ENCODE
  2656. 2656      *
  2657. 2657      * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}.
  2658. 2658      *
  2659. 2659      * @access private
  2660. 2660      * @param string $m
  2661. 2661      * @param int $emBits
  2662. 2662      */
  2663. 2663     function _emsa_pss_encode($m$emBits)
  2664. 2664     {
  2665. 2665         // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
  2666. 2666         // be output.
  2667. 2667 
  2668. 2668         $emLen = ($emBits 1) >> 3// ie. ceil($emBits / 8)
  2669. 2669         $sLen $this->sLen !== null $this->sLen $this->hLen;
  2670. 2670 
  2671. 2671         $mHash $this->hash->hash($m);
  2672. 2672         if ($emLen $this->hLen $sLen 2) {
  2673. 2673             user_error('Encoding error');
  2674. 2674             return false;
  2675. 2675         }
  2676. 2676 
  2677. 2677         $salt crypt_random_string($sLen);
  2678. 2678         $m2 "\0\0\0\0\0\0\0\0" $mHash $salt;
  2679. 2679         $h $this->hash->hash($m2);
  2680. 2680         $ps str_repeat(chr(0), $emLen $sLen $this->hLen 2);
  2681. 2681         $db $ps chr(1) . $salt;
  2682. 2682         $dbMask $this->_mgf1($h$emLen $this->hLen 1);
  2683. 2683         $maskedDB $db $dbMask;
  2684. 2684         $maskedDB[0] = ~chr(0xFF << ($emBits 7)) & $maskedDB[0];
  2685. 2685         $em $maskedDB $h chr(0xBC);
  2686. 2686 
  2687. 2687         return $em;
  2688. 2688     }
  2689. 2689 
  2690. 2690     /**
  2691. 2691      * EMSA-PSS-VERIFY
  2692. 2692      *
  2693. 2693      * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}.
  2694. 2694      *
  2695. 2695      * @access private
  2696. 2696      * @param string $m
  2697. 2697      * @param string $em
  2698. 2698      * @param int $emBits
  2699. 2699      * @return string
  2700. 2700      */
  2701. 2701     function _emsa_pss_verify($m$em$emBits)
  2702. 2702     {
  2703. 2703         // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
  2704. 2704         // be output.
  2705. 2705 
  2706. 2706         $emLen = ($emBits 1) >> 3// ie. ceil($emBits / 8);
  2707. 2707         $sLen $this->sLen !== null $this->sLen $this->hLen;
  2708. 2708 
  2709. 2709         $mHash $this->hash->hash($m);
  2710. 2710         if ($emLen $this->hLen $sLen 2) {
  2711. 2711             return false;
  2712. 2712         }
  2713. 2713 
  2714. 2714         if ($em[strlen($em) - 1] != chr(0xBC)) {
  2715. 2715             return false;
  2716. 2716         }
  2717. 2717 
  2718. 2718         $maskedDB substr($em0, -$this->hLen 1);
  2719. 2719         $h substr($em, -$this->hLen 1$this->hLen);
  2720. 2720         $temp chr(0xFF << ($emBits 7));
  2721. 2721         if ((~$maskedDB[0] & $temp) != $temp) {
  2722. 2722             return false;
  2723. 2723         }
  2724. 2724         $dbMask $this->_mgf1($h$emLen $this->hLen 1);
  2725. 2725         $db $maskedDB $dbMask;
  2726. 2726         $db[0] = ~chr(0xFF << ($emBits 7)) & $db[0];
  2727. 2727         $temp $emLen $this->hLen $sLen 2;
  2728. 2728         if (substr($db0$temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
  2729. 2729             return false;
  2730. 2730         }
  2731. 2731         $salt substr($db$temp 1); // should be $sLen long
  2732. 2732         $m2 "\0\0\0\0\0\0\0\0" $mHash $salt;
  2733. 2733         $h2 $this->hash->hash($m2);
  2734. 2734         return $this->_equals($h$h2);
  2735. 2735     }
  2736. 2736 
  2737. 2737     /**
  2738. 2738      * RSASSA-PSS-SIGN
  2739. 2739      *
  2740. 2740      * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}.
  2741. 2741      *
  2742. 2742      * @access private
  2743. 2743      * @param string $m
  2744. 2744      * @return string
  2745. 2745      */
  2746. 2746     function _rsassa_pss_sign($m)
  2747. 2747     {
  2748. 2748         // EMSA-PSS encoding
  2749. 2749 
  2750. 2750         $em $this->_emsa_pss_encode($m$this->1);
  2751. 2751 
  2752. 2752         // RSA signature
  2753. 2753 
  2754. 2754         $m $this->_os2ip($em);
  2755. 2755         $s $this->_rsasp1($m);
  2756. 2756         $s $this->_i2osp($s$this->k);
  2757. 2757 
  2758. 2758         // Output the signature S
  2759. 2759 
  2760. 2760         return $s;
  2761. 2761     }
  2762. 2762 
  2763. 2763     /**
  2764. 2764      * RSASSA-PSS-VERIFY
  2765. 2765      *
  2766. 2766      * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}.
  2767. 2767      *
  2768. 2768      * @access private
  2769. 2769      * @param string $m
  2770. 2770      * @param string $s
  2771. 2771      * @return string
  2772. 2772      */
  2773. 2773     function _rsassa_pss_verify($m$s)
  2774. 2774     {
  2775. 2775         // Length checking
  2776. 2776 
  2777. 2777         if (strlen($s) != $this->k) {
  2778. 2778             user_error('Invalid signature');
  2779. 2779             return false;
  2780. 2780         }
  2781. 2781 
  2782. 2782         // RSA verification
  2783. 2783 
  2784. 2784         $modBits $this->k;
  2785. 2785 
  2786. 2786         $s2 $this->_os2ip($s);
  2787. 2787         $m2 $this->_rsavp1($s2);
  2788. 2788         if ($m2 === false) {
  2789. 2789             user_error('Invalid signature');
  2790. 2790             return false;
  2791. 2791         }
  2792. 2792         $em $this->_i2osp($m2$modBits >> 3);
  2793. 2793         if ($em === false) {
  2794. 2794             user_error('Invalid signature');
  2795. 2795             return false;
  2796. 2796         }
  2797. 2797 
  2798. 2798         // EMSA-PSS verification
  2799. 2799 
  2800. 2800         return $this->_emsa_pss_verify($m$em$modBits 1);
  2801. 2801     }
  2802. 2802 
  2803. 2803     /**
  2804. 2804      * EMSA-PKCS1-V1_5-ENCODE
  2805. 2805      *
  2806. 2806      * See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}.
  2807. 2807      *
  2808. 2808      * @access private
  2809. 2809      * @param string $m
  2810. 2810      * @param int $emLen
  2811. 2811      * @return string
  2812. 2812      */
  2813. 2813     function _emsa_pkcs1_v1_5_encode($m$emLen)
  2814. 2814     {
  2815. 2815         $h $this->hash->hash($m);
  2816. 2816         if ($h === false) {
  2817. 2817             return false;
  2818. 2818         }
  2819. 2819 
  2820. 2820         // see http://tools.ietf.org/html/rfc3447#page-43
  2821. 2821         switch ($this->hashName) {
  2822. 2822             case 'md2':
  2823. 2823                 $t pack('H*''3020300c06082a864886f70d020205000410');
  2824. 2824                 break;
  2825. 2825             case 'md5':
  2826. 2826                 $t pack('H*''3020300c06082a864886f70d020505000410');
  2827. 2827                 break;
  2828. 2828             case 'sha1':
  2829. 2829                 $t pack('H*''3021300906052b0e03021a05000414');
  2830. 2830                 break;
  2831. 2831             case 'sha256':
  2832. 2832                 $t pack('H*''3031300d060960864801650304020105000420');
  2833. 2833                 break;
  2834. 2834             case 'sha384':
  2835. 2835                 $t pack('H*''3041300d060960864801650304020205000430');
  2836. 2836                 break;
  2837. 2837             case 'sha512':
  2838. 2838                 $t pack('H*''3051300d060960864801650304020305000440');
  2839. 2839         }
  2840. 2840         $t.= $h;
  2841. 2841         $tLen strlen($t);
  2842. 2842 
  2843. 2843         if ($emLen $tLen 11) {
  2844. 2844             user_error('Intended encoded message length too short');
  2845. 2845             return false;
  2846. 2846         }
  2847. 2847 
  2848. 2848         $ps str_repeat(chr(0xFF), $emLen $tLen 3);
  2849. 2849 
  2850. 2850         $em "\0\1$ps\0$t";
  2851. 2851 
  2852. 2852         return $em;
  2853. 2853     }
  2854. 2854 
  2855. 2855     /**
  2856. 2856      * RSASSA-PKCS1-V1_5-SIGN
  2857. 2857      *
  2858. 2858      * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}.
  2859. 2859      *
  2860. 2860      * @access private
  2861. 2861      * @param string $m
  2862. 2862      * @return string
  2863. 2863      */
  2864. 2864     function _rsassa_pkcs1_v1_5_sign($m)
  2865. 2865     {
  2866. 2866         // EMSA-PKCS1-v1_5 encoding
  2867. 2867 
  2868. 2868         $em $this->_emsa_pkcs1_v1_5_encode($m$this->k);
  2869. 2869         if ($em === false) {
  2870. 2870             user_error('RSA modulus too short');
  2871. 2871             return false;
  2872. 2872         }
  2873. 2873 
  2874. 2874         // RSA signature
  2875. 2875 
  2876. 2876         $m $this->_os2ip($em);
  2877. 2877         $s $this->_rsasp1($m);
  2878. 2878         $s $this->_i2osp($s$this->k);
  2879. 2879 
  2880. 2880         // Output the signature S
  2881. 2881 
  2882. 2882         return $s;
  2883. 2883     }
  2884. 2884 
  2885. 2885     /**
  2886. 2886      * RSASSA-PKCS1-V1_5-VERIFY
  2887. 2887      *
  2888. 2888      * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}.
  2889. 2889      *
  2890. 2890      * @access private
  2891. 2891      * @param string $m
  2892. 2892      * @return string
  2893. 2893      */
  2894. 2894     function _rsassa_pkcs1_v1_5_verify($m$s)
  2895. 2895     {
  2896. 2896         // Length checking
  2897. 2897 
  2898. 2898         if (strlen($s) != $this->k) {
  2899. 2899             user_error('Invalid signature');
  2900. 2900             return false;
  2901. 2901         }
  2902. 2902 
  2903. 2903         // RSA verification
  2904. 2904 
  2905. 2905         $s $this->_os2ip($s);
  2906. 2906         $m2 $this->_rsavp1($s);
  2907. 2907         if ($m2 === false) {
  2908. 2908             user_error('Invalid signature');
  2909. 2909             return false;
  2910. 2910         }
  2911. 2911         $em $this->_i2osp($m2$this->k);
  2912. 2912         if ($em === false) {
  2913. 2913             user_error('Invalid signature');
  2914. 2914             return false;
  2915. 2915         }
  2916. 2916 
  2917. 2917         // EMSA-PKCS1-v1_5 encoding
  2918. 2918 
  2919. 2919         $em2 $this->_emsa_pkcs1_v1_5_encode($m$this->k);
  2920. 2920         if ($em2 === false) {
  2921. 2921             user_error('RSA modulus too short');
  2922. 2922             return false;
  2923. 2923         }
  2924. 2924 
  2925. 2925         // Compare
  2926. 2926         return $this->_equals($em$em2);
  2927. 2927     }
  2928. 2928 
  2929. 2929     /**
  2930. 2930      * Set Encryption Mode
  2931. 2931      *
  2932. 2932      * Valid values include CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1.
  2933. 2933      *
  2934. 2934      * @access public
  2935. 2935      * @param int $mode
  2936. 2936      */
  2937. 2937     function setEncryptionMode($mode)
  2938. 2938     {
  2939. 2939         $this->encryptionMode $mode;
  2940. 2940     }
  2941. 2941 
  2942. 2942     /**
  2943. 2943      * Set Signature Mode
  2944. 2944      *
  2945. 2945      * Valid values include CRYPT_RSA_SIGNATURE_PSS and CRYPT_RSA_SIGNATURE_PKCS1
  2946. 2946      *
  2947. 2947      * @access public
  2948. 2948      * @param int $mode
  2949. 2949      */
  2950. 2950     function setSignatureMode($mode)
  2951. 2951     {
  2952. 2952         $this->signatureMode $mode;
  2953. 2953     }
  2954. 2954 
  2955. 2955     /**
  2956. 2956      * Set public key comment.
  2957. 2957      *
  2958. 2958      * @access public
  2959. 2959      * @param string $comment
  2960. 2960      */
  2961. 2961     function setComment($comment)
  2962. 2962     {
  2963. 2963         $this->comment $comment;
  2964. 2964     }
  2965. 2965 
  2966. 2966     /**
  2967. 2967      * Get public key comment.
  2968. 2968      *
  2969. 2969      * @access public
  2970. 2970      * @return string
  2971. 2971      */
  2972. 2972     function getComment()
  2973. 2973     {
  2974. 2974         return $this->comment;
  2975. 2975     }
  2976. 2976 
  2977. 2977     /**
  2978. 2978      * Encryption
  2979. 2979      *
  2980. 2980      * Both CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1 both place limits on how long $plaintext can be.
  2981. 2981      * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
  2982. 2982      * be concatenated together.
  2983. 2983      *
  2984. 2984      * @see self::decrypt()
  2985. 2985      * @access public
  2986. 2986      * @param string $plaintext
  2987. 2987      * @return string
  2988. 2988      */
  2989. 2989     function encrypt($plaintext)
  2990. 2990     {
  2991. 2991         switch ($this->encryptionMode) {
  2992. 2992             case CRYPT_RSA_ENCRYPTION_NONE:
  2993. 2993                 $plaintext str_split($plaintext$this->k);
  2994. 2994                 $ciphertext '';
  2995. 2995                 foreach ($plaintext as $m) {
  2996. 2996                     $ciphertext.= $this->_raw_encrypt($m);
  2997. 2997                 }
  2998. 2998                 return $ciphertext;
  2999. 2999             case CRYPT_RSA_ENCRYPTION_PKCS1:
  3000. 3000                 $length $this->11;
  3001. 3001                 if ($length <= 0) {
  3002. 3002                     return false;
  3003. 3003                 }
  3004. 3004 
  3005. 3005                 $plaintext str_split($plaintext$length);
  3006. 3006                 $ciphertext '';
  3007. 3007                 foreach ($plaintext as $m) {
  3008. 3008                     $ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m);
  3009. 3009                 }
  3010. 3010                 return $ciphertext;
  3011. 3011             //case CRYPT_RSA_ENCRYPTION_OAEP:
  3012. 3012             default:
  3013. 3013                 $length $this->$this->hLen 2;
  3014. 3014                 if ($length <= 0) {
  3015. 3015                     return false;
  3016. 3016                 }
  3017. 3017 
  3018. 3018                 $plaintext str_split($plaintext$length);
  3019. 3019                 $ciphertext '';
  3020. 3020                 foreach ($plaintext as $m) {
  3021. 3021                     $ciphertext.= $this->_rsaes_oaep_encrypt($m);
  3022. 3022                 }
  3023. 3023                 return $ciphertext;
  3024. 3024         }
  3025. 3025     }
  3026. 3026 
  3027. 3027     /**
  3028. 3028      * Decryption
  3029. 3029      *
  3030. 3030      * @see self::encrypt()
  3031. 3031      * @access public
  3032. 3032      * @param string $plaintext
  3033. 3033      * @return string
  3034. 3034      */
  3035. 3035     function decrypt($ciphertext)
  3036. 3036     {
  3037. 3037         if ($this-><= 0) {
  3038. 3038             return false;
  3039. 3039         }
  3040. 3040 
  3041. 3041         $ciphertext str_split($ciphertext$this->k);
  3042. 3042         $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->kchr(0), STR_PAD_LEFT);
  3043. 3043 
  3044. 3044         $plaintext '';
  3045. 3045 
  3046. 3046         switch ($this->encryptionMode) {
  3047. 3047             case CRYPT_RSA_ENCRYPTION_NONE:
  3048. 3048                 $decrypt '_raw_encrypt';
  3049. 3049                 break;
  3050. 3050             case CRYPT_RSA_ENCRYPTION_PKCS1:
  3051. 3051                 $decrypt '_rsaes_pkcs1_v1_5_decrypt';
  3052. 3052                 break;
  3053. 3053             //case CRYPT_RSA_ENCRYPTION_OAEP:
  3054. 3054             default:
  3055. 3055                 $decrypt '_rsaes_oaep_decrypt';
  3056. 3056         }
  3057. 3057 
  3058. 3058         foreach ($ciphertext as $c) {
  3059. 3059             $temp $this->$decrypt($c);
  3060. 3060             if ($temp === false) {
  3061. 3061                 return false;
  3062. 3062             }
  3063. 3063             $plaintext.= $temp;
  3064. 3064         }
  3065. 3065 
  3066. 3066         return $plaintext;
  3067. 3067     }
  3068. 3068 
  3069. 3069     /**
  3070. 3070      * Create a signature
  3071. 3071      *
  3072. 3072      * @see self::verify()
  3073. 3073      * @access public
  3074. 3074      * @param string $message
  3075. 3075      * @return string
  3076. 3076      */
  3077. 3077     function sign($message)
  3078. 3078     {
  3079. 3079         if (empty($this->modulus) || empty($this->exponent)) {
  3080. 3080             return false;
  3081. 3081         }
  3082. 3082 
  3083. 3083         switch ($this->signatureMode) {
  3084. 3084             case CRYPT_RSA_SIGNATURE_PKCS1:
  3085. 3085                 return $this->_rsassa_pkcs1_v1_5_sign($message);
  3086. 3086             //case CRYPT_RSA_SIGNATURE_PSS:
  3087. 3087             default:
  3088. 3088                 return $this->_rsassa_pss_sign($message);
  3089. 3089         }
  3090. 3090     }
  3091. 3091 
  3092. 3092     /**
  3093. 3093      * Verifies a signature
  3094. 3094      *
  3095. 3095      * @see self::sign()
  3096. 3096      * @access public
  3097. 3097      * @param string $message
  3098. 3098      * @param string $signature
  3099. 3099      * @return bool
  3100. 3100      */
  3101. 3101     function verify($message$signature)
  3102. 3102     {
  3103. 3103         if (empty($this->modulus) || empty($this->exponent)) {
  3104. 3104             return false;
  3105. 3105         }
  3106. 3106 
  3107. 3107         switch ($this->signatureMode) {
  3108. 3108             case CRYPT_RSA_SIGNATURE_PKCS1:
  3109. 3109                 return $this->_rsassa_pkcs1_v1_5_verify($message$signature);
  3110. 3110             //case CRYPT_RSA_SIGNATURE_PSS:
  3111. 3111             default:
  3112. 3112                 return $this->_rsassa_pss_verify($message$signature);
  3113. 3113         }
  3114. 3114     }
  3115. 3115 
  3116. 3116     /**
  3117. 3117      * Extract raw BER from Base64 encoding
  3118. 3118      *
  3119. 3119      * @access private
  3120. 3120      * @param string $str
  3121. 3121      * @return string
  3122. 3122      */
  3123. 3123     function _extractBER($str)
  3124. 3124     {
  3125. 3125         /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them
  3126. 3126          * above and beyond the ceritificate.
  3127. 3127          * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line:
  3128. 3128          *
  3129. 3129          * Bag Attributes
  3130. 3130          *     localKeyID: 01 00 00 00
  3131. 3131          * subject=/O=organization/OU=org unit/CN=common name
  3132. 3132          * issuer=/O=organization/CN=common name
  3133. 3133          */
  3134. 3134         $temp preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms'''$str1);
  3135. 3135         // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
  3136. 3136         $temp preg_replace('#-+[^-]+-+#'''$temp);
  3137. 3137         // remove new lines
  3138. 3138         $temp str_replace(array("\r""\n"' '), ''$temp);
  3139. 3139         $temp preg_match('#^[a-zA-Z\d/+]*={0,2}$#'$temp) ? base64_decode($temp) : false;
  3140. 3140         return $temp != false $temp $str;
  3141. 3141     }
  3142. 3142 }